[editor] Refactor editor dir architecture based on official guidance

This commit is contained in:
Jaylen Bian 2021-07-09 22:42:30 +08:00
parent e239fd7a40
commit ef11bbbb2b
39 changed files with 71 additions and 120 deletions

View File

@ -1,32 +1,44 @@
PODS:
- flowy_editor (0.0.1):
- FlutterMacOS
- flowy_sdk (0.0.1):
- FlutterMacOS
- FlutterMacOS (1.0.0)
- path_provider_macos (0.0.1):
- FlutterMacOS
- url_launcher_macos (0.0.1):
- FlutterMacOS
- window_size (0.0.2):
- FlutterMacOS
DEPENDENCIES:
- flowy_editor (from `Flutter/ephemeral/.symlinks/plugins/flowy_editor/macos`)
- flowy_sdk (from `Flutter/ephemeral/.symlinks/plugins/flowy_sdk/macos`)
- FlutterMacOS (from `Flutter/ephemeral`)
- path_provider_macos (from `Flutter/ephemeral/.symlinks/plugins/path_provider_macos/macos`)
- url_launcher_macos (from `Flutter/ephemeral/.symlinks/plugins/url_launcher_macos/macos`)
- window_size (from `Flutter/ephemeral/.symlinks/plugins/window_size/macos`)
EXTERNAL SOURCES:
flowy_editor:
:path: Flutter/ephemeral/.symlinks/plugins/flowy_editor/macos
flowy_sdk:
:path: Flutter/ephemeral/.symlinks/plugins/flowy_sdk/macos
FlutterMacOS:
:path: Flutter/ephemeral
path_provider_macos:
:path: Flutter/ephemeral/.symlinks/plugins/path_provider_macos/macos
url_launcher_macos:
:path: Flutter/ephemeral/.symlinks/plugins/url_launcher_macos/macos
window_size:
:path: Flutter/ephemeral/.symlinks/plugins/window_size/macos
SPEC CHECKSUMS:
flowy_editor: 26060a984848e6afac1f6a4455511f4114119d8d
flowy_sdk: c302ac0a22dea596db0df8073b9637b2bf2ff6fd
FlutterMacOS: 57701585bf7de1b3fc2bb61f6378d73bbdea8424
path_provider_macos: a0a3fd666cb7cd0448e936fb4abad4052961002b
url_launcher_macos: 45af3d61de06997666568a7149c1be98b41c95d4
window_size: 339dafa0b27a95a62a843042038fa6c3c48de195
PODFILE CHECKSUM: 6eac6b3292e5142cfc23bdeb71848a40ec51c14c

View File

@ -2,7 +2,6 @@ import 'dart:convert';
import 'dart:io';
import 'package:flowy_editor/flowy_editor.dart';
import 'package:flowy_editor/service/controller.dart';
import 'package:flutter/material.dart';
import 'package:flutter/services.dart';

View File

@ -1,8 +1,9 @@
library flowy_editor;
export 'widget/editor.dart';
export 'widget/builder.dart';
export 'widget/toolbar.dart';
export 'widget/flowy_toolbar.dart';
export 'service/style.dart';
export 'model/document/document.dart';
export 'src/widget/editor.dart';
export 'src/widget/builder.dart';
export 'src/widget/toolbar.dart';
export 'src/widget/flowy_toolbar.dart';
export 'src/service/style.dart';
export 'src/service/controller.dart';
export 'src/model/document/document.dart';

View File

@ -1,10 +1,10 @@
import 'dart:math' as math;
import 'package:flowy_editor/widget/selection.dart';
import 'package:flutter/foundation.dart';
import 'package:flutter/material.dart';
import 'package:flutter/rendering.dart';
import '../widget/selection.dart';
import '../model/document/node/container.dart' as node;
import '../model/document/document.dart';
import 'box.dart';
@ -25,8 +25,7 @@ abstract class RenderAbstractEditor {
TextPosition getPositionForOffset(Offset offset);
List<TextSelectionPoint> getEndpointsForSelection(
TextSelection textSelection);
List<TextSelectionPoint> getEndpointsForSelection(TextSelection textSelection);
void handleTapDown(TapDownDetails details);
@ -43,15 +42,12 @@ abstract class RenderAbstractEditor {
/* ------------------------------ Container Box ----------------------------- */
class EditableContainerParentData
extends ContainerBoxParentData<RenderEditableBox> {}
class EditableContainerParentData extends ContainerBoxParentData<RenderEditableBox> {}
class RenderEditableContainerBox extends RenderBox
with
ContainerRenderObjectMixin<RenderEditableBox,
EditableContainerParentData>,
RenderBoxContainerDefaultsMixin<RenderEditableBox,
EditableContainerParentData> {
ContainerRenderObjectMixin<RenderEditableBox, EditableContainerParentData>,
RenderBoxContainerDefaultsMixin<RenderEditableBox, EditableContainerParentData> {
RenderEditableContainerBox(
List<RenderEditableBox>? children,
this.textDirection,
@ -165,9 +161,7 @@ class RenderEditableContainerBox extends RenderBox
var mainAxisExtent = _resolvedPadding!.top;
var child = firstChild;
final innerConstraints =
BoxConstraints.tightFor(width: constraints.maxWidth)
.deflate(_resolvedPadding!);
final innerConstraints = BoxConstraints.tightFor(width: constraints.maxWidth).deflate(_resolvedPadding!);
while (child != null) {
child.layout(innerConstraints, parentUsesSize: true);
final childParentData = (child.parentData as EditableContainerParentData)
@ -208,11 +202,8 @@ class RenderEditableContainerBox extends RenderBox
double computeMinIntrinsicWidth(double height) {
_resolvePadding();
return _getIntrinsicCrossAxis((child) {
final childHeight = math.max<double>(
0, height - _resolvedPadding!.top + _resolvedPadding!.bottom);
return child.getMinIntrinsicWidth(childHeight) +
_resolvedPadding!.left +
_resolvedPadding!.right;
final childHeight = math.max<double>(0, height - _resolvedPadding!.top + _resolvedPadding!.bottom);
return child.getMinIntrinsicWidth(childHeight) + _resolvedPadding!.left + _resolvedPadding!.right;
});
}
@ -220,11 +211,8 @@ class RenderEditableContainerBox extends RenderBox
double computeMaxIntrinsicWidth(double height) {
_resolvePadding();
return _getIntrinsicCrossAxis((child) {
final childHeight = math.max<double>(
0, height - _resolvedPadding!.top + _resolvedPadding!.bottom);
return child.getMaxIntrinsicWidth(childHeight) +
_resolvedPadding!.left +
_resolvedPadding!.right;
final childHeight = math.max<double>(0, height - _resolvedPadding!.top + _resolvedPadding!.bottom);
return child.getMaxIntrinsicWidth(childHeight) + _resolvedPadding!.left + _resolvedPadding!.right;
});
}
@ -232,11 +220,8 @@ class RenderEditableContainerBox extends RenderBox
double computeMinIntrinsicHeight(double width) {
_resolvePadding();
return _getIntrinsicMainAxis((child) {
final childWidth = math.max<double>(
0, width - _resolvedPadding!.left + _resolvedPadding!.right);
return child.getMinIntrinsicHeight(childWidth) +
_resolvedPadding!.top +
_resolvedPadding!.bottom;
final childWidth = math.max<double>(0, width - _resolvedPadding!.left + _resolvedPadding!.right);
return child.getMinIntrinsicHeight(childWidth) + _resolvedPadding!.top + _resolvedPadding!.bottom;
});
}
@ -244,26 +229,21 @@ class RenderEditableContainerBox extends RenderBox
double computeMaxIntrinsicHeight(double width) {
_resolvePadding();
return _getIntrinsicMainAxis((child) {
final childWidth = math.max<double>(
0, width - _resolvedPadding!.left + _resolvedPadding!.right);
return child.getMaxIntrinsicHeight(childWidth) +
_resolvedPadding!.top +
_resolvedPadding!.bottom;
final childWidth = math.max<double>(0, width - _resolvedPadding!.left + _resolvedPadding!.right);
return child.getMaxIntrinsicHeight(childWidth) + _resolvedPadding!.top + _resolvedPadding!.bottom;
});
}
@override
double? computeDistanceToActualBaseline(TextBaseline baseline) {
_resolvePadding();
return defaultComputeDistanceToFirstActualBaseline(baseline)! +
_resolvedPadding!.top;
return defaultComputeDistanceToFirstActualBaseline(baseline)! + _resolvedPadding!.top;
}
}
/* ------------------------------ Render Editor ----------------------------- */
class RenderEditor extends RenderEditableContainerBox
implements RenderAbstractEditor {
class RenderEditor extends RenderEditableContainerBox implements RenderAbstractEditor {
RenderEditor(
List<RenderEditableBox>? children,
TextDirection textDirection,
@ -287,8 +267,7 @@ class RenderEditor extends RenderEditableContainerBox
TextSelectionChangeHandler onSelectionChanged;
Document _document;
TextSelection _selection;
final ValueNotifier<bool> _selectionStartInViewport =
ValueNotifier<bool>(true);
final ValueNotifier<bool> _selectionStartInViewport = ValueNotifier<bool>(true);
final ValueNotifier<bool> _selectionEndInViewport = ValueNotifier<bool>(true);
bool _hasFocus = false;
LayerLink _startHandleLayerLink;
@ -297,8 +276,7 @@ class RenderEditor extends RenderEditableContainerBox
Document get document => _document;
ValueListenable<bool> get selectionStartInViewport =>
_selectionStartInViewport;
ValueListenable<bool> get selectionStartInViewport => _selectionStartInViewport;
ValueListenable<bool> get selectionEndInViewport => _selectionEndInViewport;
@ -352,8 +330,7 @@ class RenderEditor extends RenderEditableContainerBox
}
@override
List<TextSelectionPoint> getEndpointsForSelection(
TextSelection textSelection) {
List<TextSelectionPoint> getEndpointsForSelection(TextSelection textSelection) {
if (textSelection.isCollapsed) {
final child = childAtPosition(textSelection.extent);
final localPosition = TextPosition(
@ -363,9 +340,7 @@ class RenderEditor extends RenderEditableContainerBox
final parentData = child.parentData as BoxParentData;
return [
TextSelectionPoint(
Offset(0, child.preferredLineHeight(localPosition)) +
localOffset +
parentData.offset,
Offset(0, child.preferredLineHeight(localPosition)) + localOffset + parentData.offset,
null,
)
];
@ -382,8 +357,7 @@ class RenderEditor extends RenderEditableContainerBox
assert(baseChild != null);
final baseParentData = baseChild!.parentData as BoxParentData;
final baseSelection =
localSelection(baseChild.container, textSelection, true);
final baseSelection = localSelection(baseChild.container, textSelection, true);
var basePoint = baseChild.getBaseEndpointForSelection(baseSelection);
basePoint = TextSelectionPoint(
basePoint.point + baseParentData.offset,
@ -401,10 +375,8 @@ class RenderEditor extends RenderEditableContainerBox
assert(extentChild != null);
final extentParentData = extentChild!.parentData as BoxParentData;
final extentSelection =
localSelection(extentChild.container, textSelection, true);
var extentPoint =
extentChild.getExtentEndpointForSelection(extentSelection);
final extentSelection = localSelection(extentChild.container, textSelection, true);
var extentPoint = extentChild.getExtentEndpointForSelection(extentSelection);
extentPoint = TextSelectionPoint(
extentPoint.point + extentParentData.offset,
extentPoint.direction,
@ -499,8 +471,7 @@ class RenderEditor extends RenderEditableContainerBox
affinity: position.affinity,
);
final localWord = child.getWordBoundary(localPosition);
final word = TextRange(
start: localWord.start + nodeOffset, end: localWord.end + nodeOffset);
final word = TextRange(start: localWord.start + nodeOffset, end: localWord.end + nodeOffset);
if (position.offset >= word.end) {
return TextSelection.fromPosition(position);
}
@ -523,24 +494,20 @@ class RenderEditor extends RenderEditableContainerBox
end: localWord.end + nodeOffset,
);
if (position.offset - word.start <= 1) {
_handleSelectionChange(
TextSelection.collapsed(offset: word.start), cause);
_handleSelectionChange(TextSelection.collapsed(offset: word.start), cause);
} else {
_handleSelectionChange(
TextSelection.collapsed(
offset: word.end, affinity: TextAffinity.upstream),
TextSelection.collapsed(offset: word.end, affinity: TextAffinity.upstream),
cause,
);
}
}
@override
void selectWordsInRange(
Offset from, Offset? to, SelectionChangedCause cause) {
void selectWordsInRange(Offset from, Offset? to, SelectionChangedCause cause) {
final firstPosition = getPositionForOffset(from);
final firstWord = selectWordAtPosition(firstPosition);
final lastWord =
to == null ? firstWord : selectWordAtPosition(getPositionForOffset(to));
final lastWord = to == null ? firstWord : selectWordAtPosition(getPositionForOffset(to));
_handleSelectionChange(
TextSelection(
@ -567,8 +534,7 @@ class RenderEditor extends RenderEditableContainerBox
/// The offset is the distance from the top of the editor and is the minimum
/// from the current scroll position until [selection] becomes visible.
/// Returns null if [selection] is already visible.
double? getOffsetToRevealCursor(
double viewportHeight, double scrollOffset, double offsetInViewport) {
double? getOffsetToRevealCursor(double viewportHeight, double scrollOffset, double offsetInViewport) {
final endpoints = getEndpointsForSelection(_selection);
final endpoint = endpoints.first;
final child = childAtPosition(_selection.extent);
@ -577,13 +543,8 @@ class RenderEditor extends RenderEditableContainerBox
final lineHeight = child.preferredLineHeight(
TextPosition(offset: _selection.extentOffset - child.container.offset),
);
final caretTop = endpoint.point.dy -
lineHeight -
kMargin +
offsetInViewport +
scrollBottomInset;
final caretBottom =
endpoint.point.dy + kMargin + offsetInViewport + scrollBottomInset;
final caretTop = endpoint.point.dy - lineHeight - kMargin + offsetInViewport + scrollBottomInset;
final caretBottom = endpoint.point.dy + kMargin + offsetInViewport + scrollBottomInset;
double? dy;
if (caretTop < scrollOffset) {
dy = caretTop;
@ -598,21 +559,15 @@ class RenderEditor extends RenderEditableContainerBox
// Util
void _handleSelectionChange(
TextSelection nextSelection, SelectionChangedCause cause) {
final focusingEmpty = nextSelection.baseOffset == 0 &&
nextSelection.extentOffset == 0 &&
!_hasFocus;
if (nextSelection == _selection &&
cause != SelectionChangedCause.keyboard &&
!focusingEmpty) {
void _handleSelectionChange(TextSelection nextSelection, SelectionChangedCause cause) {
final focusingEmpty = nextSelection.baseOffset == 0 && nextSelection.extentOffset == 0 && !_hasFocus;
if (nextSelection == _selection && cause != SelectionChangedCause.keyboard && !focusingEmpty) {
return;
}
onSelectionChanged(nextSelection, cause);
}
void _paintHandleLayers(
PaintingContext context, List<TextSelectionPoint> endpoints) {
void _paintHandleLayers(PaintingContext context, List<TextSelectionPoint> endpoints) {
var startPoint = endpoints[0].point;
startPoint = Offset(
startPoint.dx.clamp(0.0, size.width),

View File

@ -1,6 +1,7 @@
import 'package:flowy_editor/rendering/box.dart';
import 'package:flutter/rendering.dart';
import '../rendering/box.dart';
/* -------------------------------- Baseline -------------------------------- */
class RenderBaselineProxy extends RenderProxyBox {
@ -11,8 +12,7 @@ class RenderBaselineProxy extends RenderProxyBox {
) : _prototypePainter = TextPainter(
text: TextSpan(text: ' ', style: textStyle),
textDirection: TextDirection.ltr,
strutStyle:
StrutStyle.fromTextStyle(textStyle, forceStrutHeight: true),
strutStyle: StrutStyle.fromTextStyle(textStyle, forceStrutHeight: true),
),
super(child);
@ -55,16 +55,12 @@ class RenderEmbedProxy extends RenderProxyBox implements RenderContentProxyBox {
@override
List<TextBox> getBoxesForSelection(TextSelection textSelection) {
if (!textSelection.isCollapsed) {
return <TextBox>[
TextBox.fromLTRBD(0, 0, size.width, size.height, TextDirection.ltr)
];
return <TextBox>[TextBox.fromLTRBD(0, 0, size.width, size.height, TextDirection.ltr)];
}
final left = textSelection.extentOffset == 0 ? 0.0 : size.width;
final right = textSelection.extentOffset == 0 ? 0.0 : size.width;
return <TextBox>[
TextBox.fromLTRBD(left, 0, right, size.height, TextDirection.ltr)
];
return <TextBox>[TextBox.fromLTRBD(left, 0, right, size.height, TextDirection.ltr)];
}
@override
@ -82,8 +78,7 @@ class RenderEmbedProxy extends RenderProxyBox implements RenderContentProxyBox {
}
@override
TextRange getWordBoundary(TextPosition position) =>
const TextRange(start: 0, end: 1);
TextRange getWordBoundary(TextPosition position) => const TextRange(start: 0, end: 1);
@override
double getPreferredLineHeight() => size.height;
@ -91,8 +86,7 @@ class RenderEmbedProxy extends RenderProxyBox implements RenderContentProxyBox {
/* ---------------------------- Paragraph / Text ---------------------------- */
class RenderParagraphProxy extends RenderProxyBox
implements RenderContentProxyBox {
class RenderParagraphProxy extends RenderProxyBox implements RenderContentProxyBox {
RenderParagraphProxy(
RenderParagraph? child,
TextStyle textStyle,
@ -194,25 +188,20 @@ class RenderParagraphProxy extends RenderProxyBox
child!.getOffsetForCaret(position, caretPrototype!);
@override
TextPosition getPositionForOffset(Offset offset) =>
child!.getPositionForOffset(offset);
TextPosition getPositionForOffset(Offset offset) => child!.getPositionForOffset(offset);
@override
double? getFullHeightForCaret(TextPosition position) =>
child!.getFullHeightForCaret(position);
double? getFullHeightForCaret(TextPosition position) => child!.getFullHeightForCaret(position);
@override
TextRange getWordBoundary(TextPosition position) =>
child!.getWordBoundary(position);
TextRange getWordBoundary(TextPosition position) => child!.getWordBoundary(position);
@override
List<TextBox> getBoxesForSelection(TextSelection selection) =>
child!.getBoxesForSelection(selection);
List<TextBox> getBoxesForSelection(TextSelection selection) => child!.getBoxesForSelection(selection);
@override
void performLayout() {
super.performLayout();
_prototypePainter.layout(
minWidth: constraints.minWidth, maxWidth: constraints.maxWidth);
_prototypePainter.layout(minWidth: constraints.minWidth, maxWidth: constraints.maxWidth);
}
}

View File

@ -63,9 +63,7 @@ class EditorTextSelectionGestureDetectorBuilder {
getRenderEditor()!.handleTapDown(details);
final kind = details.kind;
shouldShowSelectionToolbar = kind == null ||
kind == PointerDeviceKind.touch ||
kind == PointerDeviceKind.stylus;
shouldShowSelectionToolbar = kind == null || kind == PointerDeviceKind.touch || kind == PointerDeviceKind.stylus;
}
void onTapUp(TapUpDetails details) {
@ -119,8 +117,7 @@ class EditorTextSelectionGestureDetectorBuilder {
);
}
void onDragSelectionUpdate(
DragStartDetails startDetails, DragUpdateDetails updateDetails) {
void onDragSelectionUpdate(DragStartDetails startDetails, DragUpdateDetails updateDetails) {
getRenderEditor()!.selectPositionAt(
startDetails.globalPosition,
updateDetails.globalPosition,
@ -135,8 +132,7 @@ class EditorTextSelectionGestureDetectorBuilder {
onTapUp: onTapUp,
onTapDown: onTapDown,
onTapCancel: onTapCancel,
onForcePressStart:
delegate.getForcePressEnabled() ? onForcePressStart : null,
onForcePressStart: delegate.getForcePressEnabled() ? onForcePressStart : null,
onForcePressEnd: delegate.getForcePressEnabled() ? onForcePressEnd : null,
onLongPressStart: onLongPressStart,
onLongPressMoveUpdate: onLongPressMoveUpdate,
@ -163,8 +159,7 @@ class EmbedBuilder {
case kImageTypeKey:
return _generateImageEmbed(context, node);
default:
throw UnimplementedError(
'Embeddable type "${node.value.type}" is not supported by default embed '
throw UnimplementedError('Embeddable type "${node.value.type}" is not supported by default embed '
'builder of QuillEditor. You must pass your own builder function to '
'embedBuilder property of QuillEditor or QuillField widgets.');
}

View File

@ -2,7 +2,6 @@ import 'dart:io' as io;
import 'dart:convert';
import 'dart:ui';
import 'package:flowy_editor/widget/image_viewer_screen.dart';
import 'package:flutter/cupertino.dart';
import 'package:flutter/material.dart';
import 'package:url_launcher/url_launcher.dart';
@ -11,6 +10,7 @@ import 'package:string_validator/string_validator.dart';
import '../widget/raw_editor.dart';
import '../widget/builder.dart';
import '../widget/proxy.dart';
import '../widget/image_viewer_screen.dart';
import '../model/document/attribute.dart';
import '../model/document/document.dart';
import '../model/document/node/embed.dart';