Skip to content

Commit

Permalink
feat: adjust the loading large documen optimization in mobile platform
Browse files Browse the repository at this point in the history
  • Loading branch information
LucasXu0 committed Sep 17, 2023
1 parent 1944ab7 commit a753a05
Show file tree
Hide file tree
Showing 3 changed files with 70 additions and 132 deletions.
23 changes: 11 additions & 12 deletions lib/src/editor/editor_component/service/scroll_service_widget.dart
Original file line number Diff line number Diff line change
Expand Up @@ -109,22 +109,21 @@ class _ScrollServiceWidgetState extends State<ScrollServiceWidget>
.jumpTo(offset: endTouchPoint.dy - 100);
}

if (selection.isCollapsed) {
if (PlatformExtension.isMobile) {
// soft keyboard
// workaround: wait for the soft keyboard to show up
return Future.delayed(const Duration(milliseconds: 300), () {
startAutoScroll(endTouchPoint, edgeOffset: 50);
});
}

if (PlatformExtension.isMobile) {
// soft keyboard
// workaround: wait for the soft keyboard to show up
return Future.delayed(const Duration(milliseconds: 300), () {
startAutoScroll(
endTouchPoint,
edgeOffset: 50,
duration: Duration.zero,
);
});
} else {
startAutoScroll(
endTouchPoint,
edgeOffset: 100,
duration: Duration.zero,
);
} else {
startAutoScroll(endTouchPoint, duration: Duration.zero);
}
});
}
Expand Down
Original file line number Diff line number Diff line change
@@ -1,11 +1,10 @@
import 'package:appflowy_editor/appflowy_editor.dart';
import 'package:appflowy_editor/src/flutter/overlay.dart';
import 'package:appflowy_editor/src/render/selection/cursor_widget.dart';
import 'package:appflowy_editor/src/render/selection/mobile_selection_widget.dart';
import 'package:appflowy_editor/src/render/selection/selection_widget.dart';
import 'package:appflowy_editor/src/service/selection/mobile_selection_gesture.dart';
import 'package:flutter/material.dart' hide Overlay, OverlayEntry;

import 'package:appflowy_editor/src/render/selection/cursor_widget.dart';
import 'package:appflowy_editor/src/render/selection/selection_widget.dart';
import 'package:provider/provider.dart';

enum MobileSelectionDragMode {
Expand Down Expand Up @@ -42,8 +41,6 @@ class _MobileSelectionServiceWidgetState
extends State<MobileSelectionServiceWidget>
with WidgetsBindingObserver
implements AppFlowySelectionService {
final _cursorKey = GlobalKey(debugLabel: 'cursor');

@override
final List<Rect> selectionRects = [];
final List<OverlayEntry> _selectionAreas = [];
Expand Down Expand Up @@ -109,20 +106,18 @@ class _MobileSelectionServiceWidgetState
_clearSelection();

if (selection != null) {
if (selection.isCollapsed) {
// updates cursor area.
Log.selection.debug('update cursor area, $selection');
_forceShowCursor();
_updateCursorAreas(selection.start);
} else {
if (!selection.isCollapsed) {
// updates selection area.
Log.selection.debug('update cursor area, $selection');
_updateSelectionAreas(selection);
}
}

currentSelection.value = selection;
editorState.selection = selection;
editorState.updateSelectionWithReason(
selection,
reason: SelectionUpdateReason.uiEvent,
);
}

@override
Expand Down Expand Up @@ -152,8 +147,8 @@ class _MobileSelectionServiceWidgetState

@override
Node? getNodeInOffset(Offset offset) {
final sortedNodes =
editorState.document.root.children.toList(growable: false);
final List<Node> sortedNodes = getVisibleNodes();

return _getNodeInOffset(
sortedNodes,
offset,
Expand All @@ -162,6 +157,30 @@ class _MobileSelectionServiceWidgetState
);
}

List<Node> getVisibleNodes() {
final List<Node> sortedNodes = [];
final positions =
context.read<EditorScrollController>().visibleRangeNotifier.value;
final min = positions.$1;
final max = positions.$2;
if (min < 0 || max < 0) {
return sortedNodes;
}

int i = -1;
for (final child in editorState.document.root.children) {
i++;
if (min > i) {
continue;
}
if (i > max) {
break;
}
sortedNodes.add(child);
}
return sortedNodes;
}

@override
Position? getPositionInOffset(Offset offset) {
final node = getNodeInOffset(offset);
Expand All @@ -185,43 +204,21 @@ class _MobileSelectionServiceWidgetState

void _updateSelection() {
final selection = editorState.selection;
// TODO: why do we need to check this?
if (currentSelection.value == selection &&
editorState.selectionUpdateReason == SelectionUpdateReason.uiEvent &&
editorState.selectionType != SelectionType.block) {
if (currentSelection.value != selection) {
return;
}

currentSelection.value = selection;

void updateSelection() {
selectionRects.clear();
_clearSelection();

if (selection != null) {
if (editorState.selectionType == SelectionType.block) {
// updates selection area.
Log.selection.debug('update block selection area, $selection');
_updateBlockSelectionAreas(selection);
} else if (selection.isCollapsed) {
// updates cursor area.
Log.selection.debug('update cursor area, $selection');
_updateCursorAreas(selection.start);
} else {
// updates selection area.
Log.selection.debug('update selection area, $selection');
if (selection != null) {
if (!selection.isCollapsed) {
// updates selection area.
Log.selection.debug('update cursor area, $selection');
WidgetsBinding.instance.addPostFrameCallback((timeStamp) {
selectionRects.clear();
_clearSelection();
_updateSelectionAreas(selection);
}
});
}
}

if (editorState.selectionUpdateReason == SelectionUpdateReason.uiEvent) {
updateSelection();
} else {
WidgetsBinding.instance.addPostFrameCallback((timeStamp) {
updateSelection();
});
}
}

void _onTapDown(TapDownDetails details) {
Expand All @@ -230,6 +227,8 @@ class _MobileSelectionServiceWidgetState
);
if (!canTap) return;

clearSelection();

// clear old state.
_panStartOffset = null;

Expand All @@ -238,7 +237,6 @@ class _MobileSelectionServiceWidgetState
return;
}

// updateSelection(selection);
editorState.selection = Selection.collapsed(position);
}

Expand Down Expand Up @@ -344,33 +342,6 @@ class _MobileSelectionServiceWidgetState
// do nothing
}

void _updateBlockSelectionAreas(Selection selection) {
assert(editorState.selectionType == SelectionType.block);
final nodes = editorState.getNodesInSelection(selection).normalized;

currentSelectedNodes = nodes;

final node = nodes.first;
var offset = Offset.zero;
var size = node.rect.size;
final rect = offset & size;

final overlay = OverlayEntry(
builder: (context) => MobileSelectionWidget(
color: widget.selectionColor,
layerLink: node.layerLink,
rect: rect,
decoration: BoxDecoration(
color: widget.selectionColor,
borderRadius: BorderRadius.circular(4.0),
),
),
);
_selectionAreas.add(overlay);

Overlay.of(context)?.insertAll(_selectionAreas);
}

void _updateSelectionAreas(Selection selection) {
final nodes = editorState.getNodesInSelection(selection);

Expand Down Expand Up @@ -426,18 +397,26 @@ class _MobileSelectionServiceWidgetState
}
}

final rects = selectable.getRectsInSelection(newSelection);
final rects = selectable.getRectsInSelection(
newSelection,
shiftWithBaseOffset: true,
);
for (final (j, rect) in rects.indexed) {
final selectionRect = selectable.transformRectToGlobal(rect);
final selectionRect = selectable.transformRectToGlobal(
rect,
shiftWithBaseOffset: true,
);
selectionRects.add(selectionRect);
final showLeftHandler = i == 0 && j == 0;
final showRightHandler =
i == backwardNodes.length - 1 && j == rects.length - 1;
final overlay = OverlayEntry(
builder: (context) => MobileSelectionWidget(
color: widget.selectionColor,
color: Colors.transparent,
layerLink: node.layerLink,
rect: rect,
showLeftHandler: i == 0 && j == 0,
showRightHandler:
i == backwardNodes.length - 1 && j == rects.length - 1,
showLeftHandler: showLeftHandler,
showRightHandler: showRightHandler,
),
);
_selectionAreas.add(overlay);
Expand All @@ -451,46 +430,6 @@ class _MobileSelectionServiceWidgetState
);
}

void _updateCursorAreas(Position position) {
final node = editorState.document.root.childAtPath(position.path);

if (node == null) {
assert(false);
return;
}

currentSelectedNodes = [node];

_showCursor(node, position);
}

void _showCursor(Node node, Position position) {
final selectable = node.selectable;
final cursorRect = selectable?.getCursorRectInPosition(position);
if (selectable != null && cursorRect != null) {
final cursorArea = OverlayEntry(
builder: (context) => CursorWidget(
key: _cursorKey,
rect: cursorRect,
color: widget.cursorColor,
layerLink: node.layerLink,
shouldBlink: selectable.shouldCursorBlink,
cursorStyle: selectable.cursorStyle,
),
);

_cursorAreas.add(cursorArea);
selectionRects.add(selectable.transformRectToGlobal(cursorRect));
Overlay.of(context)?.insertAll(_cursorAreas);

_forceShowCursor();
}
}

void _forceShowCursor() {
_cursorKey.currentState?.unwrapOrNull<CursorWidgetState>()?.show();
}

Node? _getNodeInOffset(
List<Node> sortedNodes,
Offset offset,
Expand Down
2 changes: 1 addition & 1 deletion lib/src/render/selection/mobile_selection_widget.dart
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@ class MobileSelectionWidget extends StatelessWidget {
child: CompositedTransformFollower(
link: layerLink,
offset: rect.topLeft,
showWhenUnlinked: true,
showWhenUnlinked: false,
// Ignore the gestures in selection overlays
// to solve the problem that selection areas cannot overlap.
child: IgnorePointer(
Expand Down

0 comments on commit a753a05

Please sign in to comment.