Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Update SuperEditor and SuperReader layer system to provide app-level composition (iOS)(Relates to #424, #893, #1166)(Resolves #1508) #1470

Merged
merged 37 commits into from
Oct 23, 2023
Merged
Show file tree
Hide file tree
Changes from 22 commits
Commits
Show all changes
37 commits
Select commit Hold shift + click to select a range
856ce6d
Generalizing mobile overlays and creating generic content anchoring s…
matthew-carroll Sep 26, 2023
35f9c91
Fixed floating cursor test after rebase
matthew-carroll Sep 26, 2023
2a8b854
Renamed a couple iOS controls context classes
matthew-carroll Sep 26, 2023
ca4774b
WIP: SuperReader uses the new layer system for selection and toolbar,…
matthew-carroll Oct 1, 2023
b360adf
Merge branch 'main' into 424_create-generic-content-anchor-system
matthew-carroll Oct 8, 2023
0d5a4bd
Merge fix: iOS long-press magnifier in wrong location upon initial se…
matthew-carroll Oct 8, 2023
d4a8eb8
Moved SuperEditor and SuperReader iOS toolbar positioning to a Leader…
matthew-carroll Oct 9, 2023
59c34f0
Migrated iOS magnifier from standard LayerLink to LeaderLink. We want…
matthew-carroll Oct 9, 2023
8663889
Changed iOS toolbar overlay to use the Overlay context - it was mista…
matthew-carroll Oct 9, 2023
58ca5c2
Fixed caret so that it stops blinking when it's being dragged. Also f…
matthew-carroll Oct 9, 2023
94eb80d
Fixed a couple mistakes that broke a few tests
matthew-carroll Oct 9, 2023
19e8e3f
Moved FloatingCursorStatus behavior into FloatingCursorController in …
matthew-carroll Oct 9, 2023
d2dbac0
Refactored the floating cursor architecture in SuperEditor. Moved mos…
matthew-carroll Oct 10, 2023
2b4e0c1
Toolbar and magnifier are now configurable via the controls scope
matthew-carroll Oct 10, 2023
661af98
Renamed IosEditorControlsContext to IosEditorControlsController
matthew-carroll Oct 10, 2023
bf4cca2
Added a LeaderLink focal point to the iOS toolbar builder, to match h…
matthew-carroll Oct 10, 2023
f561c15
Separated iOS editor controls scope from iOS reader controls scope
matthew-carroll Oct 11, 2023
dc0045f
Renamed things to started with SuperEditor or SuperReader for consist…
matthew-carroll Oct 11, 2023
4af29b0
Merge branch 'main' into 424_create-generic-content-anchor-system
matthew-carroll Oct 13, 2023
dd8b9c9
Brought toolbar, magnifier, handles inspection and tests to SuperRead…
matthew-carroll Oct 13, 2023
649bd10
Switched magnifier display from a document layer to a manager + overl…
matthew-carroll Oct 13, 2023
9ff322f
Re-organized some document overlay code
matthew-carroll Oct 13, 2023
2a346a7
Renamed SuperEditorIosControlsDocumentLayerBuilder to SuperEditorIosH…
matthew-carroll Oct 13, 2023
f6d6f81
Replaced use of ValueNotifier for magnifier and toolbar with explicit…
matthew-carroll Oct 17, 2023
6293df0
Reworked overlay management in ToolbarFollowingContentInLayer
matthew-carroll Oct 17, 2023
070b371
Remove commented code
matthew-carroll Oct 17, 2023
d316282
Removed more commented code
matthew-carroll Oct 17, 2023
79b6d1f
Fix Dart Doc typo
matthew-carroll Oct 17, 2023
e557d18
Switched caret blinking mutation from a ValueNotifier to blinkCaret()…
matthew-carroll Oct 17, 2023
1a65c25
Merge branch 'main' into 424_create-generic-content-anchor-system
matthew-carroll Oct 17, 2023
fad4712
Brought back iOS handle color and toolbar builder into SuperEditor an…
matthew-carroll Oct 17, 2023
b10a357
* A few minor PR adjustments and cleanups
matthew-carroll Oct 18, 2023
b71cf3e
Minor PR updates
matthew-carroll Oct 20, 2023
0d30d93
Made text extraction method public within infrastructure
matthew-carroll Oct 20, 2023
b943cfd
Used setStateAsSoonAsPossible() in place of an adhoc implementation, …
matthew-carroll Oct 20, 2023
3caf828
Minor PR update
matthew-carroll Oct 20, 2023
b5769b7
Merge branch 'main' into 424_create-generic-content-anchor-system
matthew-carroll Oct 23, 2023
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ import 'dart:io';

import 'package:example/demos/editor_configs/keyboard_overlay_clipper.dart';
import 'package:flutter/material.dart';
import 'package:follow_the_leader/follow_the_leader.dart';
import 'package:super_editor/super_editor.dart';

/// Mobile iOS document editing demo.
Expand All @@ -21,10 +22,15 @@ class _MobileEditingIOSDemoState extends State<MobileEditingIOSDemo> {
late MutableDocumentComposer _composer;
late Editor _docEditor;
late CommonEditorOperations _docOps;
late MagnifierAndToolbarController _overlayController;

FocusNode? _editorFocusNode;

final _selectionLayerLinks = SelectionLayerLinks();

// TODO: get rid of overlay controller once Android is refactored to use a control scope (as follow up to: https://github.com/superlistapp/super_editor/pull/1470)
late MagnifierAndToolbarController _overlayController;
late final SuperEditorIosControlsController _iosEditorControlsController;

@override
void initState() {
super.initState();
Expand All @@ -38,11 +44,19 @@ class _MobileEditingIOSDemoState extends State<MobileEditingIOSDemo> {
documentLayoutResolver: () => _docLayoutKey.currentState as DocumentLayout,
);
_editorFocusNode = FocusNode();

// TODO: get rid of the overlay controller
_overlayController = MagnifierAndToolbarController();
_iosEditorControlsController = SuperEditorIosControlsController(
toolbarBuilder: _buildIosToolbar,
magnifierBuilder: _buildIosMagnifier,
);
}

@override
void dispose() {
_iosEditorControlsController.dispose();

_editorFocusNode!.dispose();
_composer.dispose();
_doc.removeListener(_onDocumentChange);
Expand All @@ -53,17 +67,23 @@ class _MobileEditingIOSDemoState extends State<MobileEditingIOSDemo> {

void _cut() {
_docOps.cut();
// TODO: get rid of overlay controller once Android is refactored to use a control scope (as follow up to: https://github.com/superlistapp/super_editor/pull/1470)
_overlayController.hideToolbar();
_iosEditorControlsController.shouldShowToolbar.value = false;
matthew-carroll marked this conversation as resolved.
Show resolved Hide resolved
}

void _copy() {
_docOps.copy();
// TODO: get rid of overlay controller once Android is refactored to use a control scope (as follow up to: https://github.com/superlistapp/super_editor/pull/1470)
_overlayController.hideToolbar();
_iosEditorControlsController.shouldShowToolbar.value = false;
}

void _paste() {
_docOps.paste();
// TODO: get rid of overlay controller once Android is refactored to use a control scope (as follow up to: https://github.com/superlistapp/super_editor/pull/1470)
_overlayController.hideToolbar();
_iosEditorControlsController.shouldShowToolbar.value = false;
}

@override
Expand All @@ -72,25 +92,23 @@ class _MobileEditingIOSDemoState extends State<MobileEditingIOSDemo> {
child: Column(
children: [
Expanded(
child: SuperEditor(
focusNode: _editorFocusNode,
documentLayoutKey: _docLayoutKey,
editor: _docEditor,
document: _doc,
composer: _composer,
overlayController: _overlayController,
gestureMode: DocumentGestureMode.iOS,
inputSource: TextInputSource.ime,
iOSToolbarBuilder: (_) => IOSTextEditingFloatingToolbar(
onCutPressed: _cut,
onCopyPressed: _copy,
onPastePressed: _paste,
focalPoint: _overlayController.toolbarTopAnchor!,
),
stylesheet: defaultStylesheet.copyWith(
documentPadding: const EdgeInsets.all(16),
child: SuperEditorIosControlsScope(
controller: _iosEditorControlsController,
child: SuperEditor(
focusNode: _editorFocusNode,
documentLayoutKey: _docLayoutKey,
editor: _docEditor,
document: _doc,
composer: _composer,
gestureMode: DocumentGestureMode.iOS,
inputSource: TextInputSource.ime,
selectionLayerLinks: _selectionLayerLinks,
stylesheet: defaultStylesheet.copyWith(
documentPadding: const EdgeInsets.all(16),
),
overlayController: _overlayController,
createOverlayControlsClipper: (_) => const KeyboardToolbarClipper(),
),
createOverlayControlsClipper: (_) => const KeyboardToolbarClipper(),
),
),
MultiListenableBuilder(
Expand All @@ -105,6 +123,26 @@ class _MobileEditingIOSDemoState extends State<MobileEditingIOSDemo> {
);
}

Widget _buildIosToolbar(BuildContext context, Key mobileToolbarKey, LeaderLink focalPoint) {
return IOSTextEditingFloatingToolbar(
key: mobileToolbarKey,
focalPoint: focalPoint,
onCutPressed: _cut,
onCopyPressed: _copy,
onPastePressed: _paste,
);
}

Widget _buildIosMagnifier(BuildContext context, Key magnifierKey, LeaderLink focalPoint) {
return Center(
child: IOSFollowingMagnifier.roundedRectangle(
magnifierKey: magnifierKey,
leaderLink: focalPoint,
offsetFromFocalPoint: const Offset(0, -72),
),
);
}

Widget _buildMountedToolbar() {
final selection = _composer.selection;

Expand Down
133 changes: 75 additions & 58 deletions super_editor/example/lib/demos/example_editor/example_editor.dart
Original file line number Diff line number Diff line change
Expand Up @@ -43,9 +43,12 @@ class _ExampleEditorState extends State<ExampleEditor> {
OverlayEntry? _imageFormatBarOverlayEntry;
final _imageSelectionAnchor = ValueNotifier<Offset?>(null);

// TODO: get rid of overlay controller once Android is refactored to use a control scope (as follow up to: https://github.com/superlistapp/super_editor/pull/1470)
final _overlayController = MagnifierAndToolbarController() //
..screenPadding = const EdgeInsets.all(20.0);

late final SuperEditorIosControlsController _iosControlsController;

@override
void initState() {
super.initState();
Expand All @@ -61,6 +64,8 @@ class _ExampleEditorState extends State<ExampleEditor> {
);
_editorFocusNode = FocusNode();
_scrollController = ScrollController()..addListener(_hideOrShowToolbar);

_iosControlsController = SuperEditorIosControlsController();
}

@override
Expand All @@ -69,6 +74,8 @@ class _ExampleEditorState extends State<ExampleEditor> {
_textFormatBarOverlayEntry!.remove();
}

_iosControlsController.dispose();

_scrollController.dispose();
_editorFocusNode.dispose();
_composer.dispose();
Expand Down Expand Up @@ -233,17 +240,23 @@ class _ExampleEditorState extends State<ExampleEditor> {

void _cut() {
_docOps.cut();
// TODO: get rid of overlay controller once Android is refactored to use a control scope (as follow up to: https://github.com/superlistapp/super_editor/pull/1470)
_overlayController.hideToolbar();
_iosControlsController.shouldShowToolbar.value = false;
}

void _copy() {
_docOps.copy();
// TODO: get rid of overlay controller once Android is refactored to use a control scope (as follow up to: https://github.com/superlistapp/super_editor/pull/1470)
_overlayController.hideToolbar();
_iosControlsController.shouldShowToolbar.value = false;
}

void _paste() {
_docOps.paste();
// TODO: get rid of overlay controller once Android is refactored to use a control scope (as follow up to: https://github.com/superlistapp/super_editor/pull/1470)
_overlayController.hideToolbar();
_iosControlsController.shouldShowToolbar.value = false;
}

void _selectAll() => _docOps.selectAll();
Expand Down Expand Up @@ -333,7 +346,16 @@ class _ExampleEditorState extends State<ExampleEditor> {
),
Align(
alignment: Alignment.bottomRight,
child: _buildCornerFabs(),
child: ListenableBuilder(
listenable: _composer.selectionNotifier,
builder: (context, child) {
return Padding(
padding: EdgeInsets.only(bottom: _isMobile && _composer.selection != null ? 48 : 0),
child: child,
);
},
child: _buildCornerFabs(),
),
),
],
);
Expand Down Expand Up @@ -407,72 +429,67 @@ class _ExampleEditorState extends State<ExampleEditor> {
config: _debugConfig ?? const SuperEditorDebugVisualsConfig(),
child: KeyedSubtree(
key: _viewportKey,
child: SuperEditor(
editor: _docEditor,
document: _doc,
composer: _composer,
focusNode: _editorFocusNode,
scrollController: _scrollController,
documentLayoutKey: _docLayoutKey,
documentOverlayBuilders: [
DefaultCaretOverlayBuilder(
caretStyle: const CaretStyle().copyWith(color: isLight ? Colors.black : Colors.redAccent),
child: SuperEditorIosControlsScope(
controller: _iosControlsController,
child: SuperEditor(
editor: _docEditor,
document: _doc,
composer: _composer,
focusNode: _editorFocusNode,
scrollController: _scrollController,
documentLayoutKey: _docLayoutKey,
documentOverlayBuilders: [
DefaultCaretOverlayBuilder(
caretStyle: const CaretStyle().copyWith(color: isLight ? Colors.black : Colors.redAccent),
),
SuperEditorIosToolbarFocalPointDocumentLayerBuilder(),
SuperEditorIosControlsDocumentLayerBuilder(),
],
selectionLayerLinks: _selectionLayerLinks,
selectionStyle: isLight
? defaultSelectionStyle
: SelectionStyles(
selectionColor: Colors.red.withOpacity(0.3),
),
stylesheet: defaultStylesheet.copyWith(
addRulesAfter: [
if (!isLight) ..._darkModeStyles,
taskStyles,
],
),
],
selectionLayerLinks: _selectionLayerLinks,
selectionStyle: isLight
? defaultSelectionStyle
: SelectionStyles(
selectionColor: Colors.red.withOpacity(0.3),
),
stylesheet: defaultStylesheet.copyWith(
addRulesAfter: [
if (!isLight) ..._darkModeStyles,
taskStyles,
componentBuilders: [
TaskComponentBuilder(_docEditor),
...defaultComponentBuilders,
],
gestureMode: _gestureMode,
inputSource: _inputSource,
keyboardActions: _inputSource == TextInputSource.ime ? defaultImeKeyboardActions : defaultKeyboardActions,
androidToolbarBuilder: (_) => _buildAndroidFloatingToolbar(),
overlayController: _overlayController,
),
componentBuilders: [
TaskComponentBuilder(_docEditor),
...defaultComponentBuilders,
],
gestureMode: _gestureMode,
inputSource: _inputSource,
keyboardActions: _inputSource == TextInputSource.ime ? defaultImeKeyboardActions : defaultKeyboardActions,
androidToolbarBuilder: (_) => ListenableBuilder(
listenable: _brightness,
builder: (context, _) {
return Theme(
data: ThemeData(brightness: _brightness.value),
child: AndroidTextEditingFloatingToolbar(
onCutPressed: _cut,
onCopyPressed: _copy,
onPastePressed: _paste,
onSelectAllPressed: _selectAll,
),
);
},
),
iOSToolbarBuilder: (_) => ListenableBuilder(
listenable: _brightness,
builder: (context, _) {
return Theme(
data: ThemeData(brightness: _brightness.value),
child: IOSTextEditingFloatingToolbar(
onCutPressed: _cut,
onCopyPressed: _copy,
onPastePressed: _paste,
focalPoint: _overlayController.toolbarTopAnchor!,
),
);
},
),
overlayController: _overlayController,
),
),
),
);
}

Widget _buildAndroidFloatingToolbar() {
return ListenableBuilder(
listenable: _brightness,
builder: (context, _) {
return Theme(
data: ThemeData(brightness: _brightness.value),
child: AndroidTextEditingFloatingToolbar(
onCutPressed: _cut,
onCopyPressed: _copy,
onPastePressed: _paste,
onSelectAllPressed: _selectAll,
),
);
},
);
}

Widget _buildMountedToolbar() {
return MultiListenableBuilder(
listenables: <Listenable>{
Expand Down
Loading
Loading