Apple pencil (#1740)

* Added ScribbleFocusable class and updated QuillRawEditorState build() to use it as parent widget of QuilRawEditorMultiChildRenderObject.
Added optional configuration properties `final bool enableScribble`, `final EdgeInsets? scribbleAreaInsets`, `final void Function()? onScribbleActivated`

* format update
pull/1745/head
Michael Allen 1 year ago committed by GitHub
parent d64fd9a47a
commit 2c03c5e7f2
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
  1. 18
      lib/src/models/config/editor/editor_configurations.dart
  2. 12
      lib/src/models/config/raw_editor/raw_editor_configurations.dart
  3. 3
      lib/src/widgets/editor/editor.dart
  4. 86
      lib/src/widgets/raw_editor/raw_editor_state.dart
  5. 125
      lib/src/widgets/raw_editor/scribble_focusable.dart

@ -76,6 +76,9 @@ class QuillEditorConfigurations extends Equatable {
this.builder,
this.magnifierConfiguration,
this.textInputAction = TextInputAction.newline,
this.enableScribble = false,
this.onScribbleActivated,
this.scribbleAreaInsets,
});
final QuillSharedConfigurations sharedConfigurations;
@ -336,6 +339,15 @@ class QuillEditorConfigurations extends Equatable {
/// Default to [TextInputAction.newline]
final TextInputAction textInputAction;
/// Enable Scribble? Currently Apple Pencil only, defaults to false.
final bool enableScribble;
/// Called when Scribble is activated.
final void Function()? onScribbleActivated;
/// Optional insets for the scribble area.
final EdgeInsets? scribbleAreaInsets;
@override
List<Object?> get props => [
placeholder,
@ -393,6 +405,9 @@ class QuillEditorConfigurations extends Equatable {
QuillEditorBuilder? builder,
TextMagnifierConfiguration? magnifierConfiguration,
TextInputAction? textInputAction,
bool? enableScribble,
void Function()? onScribbleActivated,
EdgeInsets? scribbleAreaInsets,
}) {
return QuillEditorConfigurations(
sharedConfigurations: sharedConfigurations ?? this.sharedConfigurations,
@ -453,6 +468,9 @@ class QuillEditorConfigurations extends Equatable {
magnifierConfiguration:
magnifierConfiguration ?? this.magnifierConfiguration,
textInputAction: textInputAction ?? this.textInputAction,
enableScribble: enableScribble ?? this.enableScribble,
onScribbleActivated: onScribbleActivated ?? this.onScribbleActivated,
scribbleAreaInsets: scribbleAreaInsets ?? this.scribbleAreaInsets,
);
}
}

@ -78,6 +78,9 @@ class QuillRawEditorConfigurations extends Equatable {
this.contentInsertionConfiguration,
this.textInputAction = TextInputAction.newline,
this.requestKeyboardFocusOnCheckListChanged = false,
this.enableScribble = false,
this.onScribbleActivated,
this.scribbleAreaInsets,
});
/// Controls the document being edited.
@ -303,6 +306,15 @@ class QuillRawEditorConfigurations extends Equatable {
final TextInputAction textInputAction;
/// Enable Scribble? Currently Apple Pencil only, defaults to false.
final bool enableScribble;
/// Called when Scribble is activated.
final void Function()? onScribbleActivated;
/// Optional insets for the scribble area.
final EdgeInsets? scribbleAreaInsets;
@override
List<Object?> get props => [
readOnly,

@ -285,6 +285,9 @@ class QuillEditorState extends State<QuillEditor>
dialogTheme: configurations.dialogTheme,
contentInsertionConfiguration:
configurations.contentInsertionConfiguration,
enableScribble: configurations.enableScribble,
onScribbleActivated: configurations.onScribbleActivated,
scribbleAreaInsets: configurations.scribbleAreaInsets,
),
),
),

@ -52,6 +52,7 @@ import 'raw_editor_render_object.dart';
import 'raw_editor_state_selection_delegate_mixin.dart';
import 'raw_editor_state_text_input_client_mixin.dart';
import 'raw_editor_text_boundaries.dart';
import 'scribble_focusable.dart';
class QuillRawEditorState extends EditorState
with
@ -480,6 +481,24 @@ class QuillRawEditorState extends EditorState
}
}
Widget _scribbleFocusable(Widget child) {
return ScribbleFocusable(
editorKey: _editorKey,
enabled: widget.configurations.enableScribble &&
!widget.configurations.readOnly,
renderBoxForBounds: () => context
.findAncestorStateOfType<QuillEditorState>()
?.context
.findRenderObject() as RenderBox?,
onScribbleFocus: (offset) {
widget.configurations.focusNode.requestFocus();
widget.configurations.onScribbleActivated?.call();
},
scribbleAreaInsets: widget.configurations.scribbleAreaInsets,
child: child,
);
}
@override
Widget build(BuildContext context) {
assert(debugCheckHasMediaQuery(context));
@ -495,34 +514,6 @@ class QuillRawEditorState extends EditorState
);
}
Widget child = CompositedTransformTarget(
link: _toolbarLayerLink,
child: Semantics(
child: MouseRegion(
cursor: SystemMouseCursors.text,
child: QuilRawEditorMultiChildRenderObject(
key: _editorKey,
document: doc,
selection: controller.selection,
hasFocus: _hasFocus,
scrollable: widget.configurations.scrollable,
cursorController: _cursorCont,
textDirection: _textDirection,
startHandleLayerLink: _startHandleLayerLink,
endHandleLayerLink: _endHandleLayerLink,
onSelectionChanged: _handleSelectionChanged,
onSelectionCompleted: _handleSelectionCompleted,
scrollBottomInset: widget.configurations.scrollBottomInset,
padding: widget.configurations.padding,
maxContentWidth: widget.configurations.maxContentWidth,
floatingCursorDisabled:
widget.configurations.floatingCursorDisabled,
children: _buildChildren(doc, context),
),
),
),
);
if (!widget.configurations.disableClipboard) {
// Web - esp Safari Mac/iOS has security measures in place that restrict
// cliboard status checks w/o direct user interaction. Initializing the
@ -536,6 +527,7 @@ class QuillRawEditorState extends EditorState
}
}
Widget child;
if (widget.configurations.scrollable) {
/// Since [SingleChildScrollView] does not implement
/// `computeDistanceToActualBaseline` it prevents the editor from
@ -555,13 +547,46 @@ class QuillRawEditorState extends EditorState
link: _toolbarLayerLink,
child: MouseRegion(
cursor: SystemMouseCursors.text,
child: QuilRawEditorMultiChildRenderObject(
child: _scribbleFocusable(
QuilRawEditorMultiChildRenderObject(
key: _editorKey,
offset: offset,
document: doc,
selection: controller.selection,
hasFocus: _hasFocus,
scrollable: widget.configurations.scrollable,
textDirection: _textDirection,
startHandleLayerLink: _startHandleLayerLink,
endHandleLayerLink: _endHandleLayerLink,
onSelectionChanged: _handleSelectionChanged,
onSelectionCompleted: _handleSelectionCompleted,
scrollBottomInset: widget.configurations.scrollBottomInset,
padding: widget.configurations.padding,
maxContentWidth: widget.configurations.maxContentWidth,
cursorController: _cursorCont,
floatingCursorDisabled:
widget.configurations.floatingCursorDisabled,
children: _buildChildren(doc, context),
),
),
),
),
),
);
} else {
child = CompositedTransformTarget(
link: _toolbarLayerLink,
child: Semantics(
child: MouseRegion(
cursor: SystemMouseCursors.text,
child: _scribbleFocusable(
QuilRawEditorMultiChildRenderObject(
key: _editorKey,
offset: offset,
document: doc,
selection: controller.selection,
hasFocus: _hasFocus,
scrollable: widget.configurations.scrollable,
cursorController: _cursorCont,
textDirection: _textDirection,
startHandleLayerLink: _startHandleLayerLink,
endHandleLayerLink: _endHandleLayerLink,
@ -570,7 +595,6 @@ class QuillRawEditorState extends EditorState
scrollBottomInset: widget.configurations.scrollBottomInset,
padding: widget.configurations.padding,
maxContentWidth: widget.configurations.maxContentWidth,
cursorController: _cursorCont,
floatingCursorDisabled:
widget.configurations.floatingCursorDisabled,
children: _buildChildren(doc, context),

@ -0,0 +1,125 @@
import 'package:flutter/material.dart';
import 'package:flutter/rendering.dart';
import 'package:flutter/services.dart';
class ScribbleFocusable extends StatefulWidget {
const ScribbleFocusable({
required this.child,
required this.editorKey,
required this.renderBoxForBounds,
required this.onScribbleFocus,
required this.enabled,
required this.scribbleAreaInsets,
super.key,
});
final Widget child;
final GlobalKey editorKey;
final RenderBox? Function() renderBoxForBounds;
final void Function(Offset offset) onScribbleFocus;
final bool enabled;
final EdgeInsets? scribbleAreaInsets;
@override
// ignore: library_private_types_in_public_api
_ScribbleFocusableState createState() => _ScribbleFocusableState();
}
class _ScribbleFocusableState extends State<ScribbleFocusable>
implements ScribbleClient {
_ScribbleFocusableState()
: _elementIdentifier = 'quill-scribble-${_nextElementIdentifier++}';
@override
void initState() {
super.initState();
if (widget.enabled) {
TextInput.registerScribbleElement(elementIdentifier, this);
}
}
@override
void didUpdateWidget(ScribbleFocusable oldWidget) {
super.didUpdateWidget(oldWidget);
if (!oldWidget.enabled && widget.enabled) {
TextInput.registerScribbleElement(elementIdentifier, this);
}
if (oldWidget.enabled && !widget.enabled) {
TextInput.unregisterScribbleElement(elementIdentifier);
}
}
@override
void dispose() {
TextInput.unregisterScribbleElement(elementIdentifier);
super.dispose();
}
RenderBox? get _renderBoxForEditor =>
widget.editorKey.currentContext?.findRenderObject() as RenderBox?;
RenderBox? get _renderBoxForBounds {
final box = widget.renderBoxForBounds();
if (box == null || !mounted || !box.attached) {
return null;
}
return box;
}
static int _nextElementIdentifier = 1;
final String _elementIdentifier;
@override
String get elementIdentifier => _elementIdentifier;
@override
void onScribbleFocus(Offset offset) {
widget.onScribbleFocus(offset);
}
@override
bool isInScribbleRect(Rect rect) {
final calculatedBounds = bounds;
if (calculatedBounds == Rect.zero) {
return false;
}
if (!calculatedBounds.overlaps(rect)) {
return false;
}
final intersection = calculatedBounds.intersect(rect);
final result = HitTestResult();
WidgetsBinding.instance
.hitTestInView(result, intersection.center, View.of(context).viewId);
return result.path.any((entry) =>
entry.target == _renderBoxForEditor ||
entry.target == _renderBoxForBounds);
}
@override
Rect get bounds {
final box = context.findRenderObject() as RenderBox?;
if (box == null || !mounted || !box.attached) {
return Rect.zero;
}
final transform = box.getTransformTo(null);
final size = _renderBoxForBounds?.size ?? box.size;
return MatrixUtils.transformRect(
transform,
Rect.fromLTWH(
0 + (widget.scribbleAreaInsets?.left ?? 0),
0 + (widget.scribbleAreaInsets?.top ?? 0),
size.width -
(widget.scribbleAreaInsets?.left ?? 0) -
(widget.scribbleAreaInsets?.right ?? 0),
size.height -
(widget.scribbleAreaInsets?.top ?? 0) -
(widget.scribbleAreaInsets?.bottom ?? 0),
));
}
@override
Widget build(BuildContext context) {
return widget.child;
}
}
Loading…
Cancel
Save