Skip to content

Commit

Permalink
fix: unable to click the items in a long list (AppFlowy-IO#725)
Browse files Browse the repository at this point in the history
  • Loading branch information
LucasXu0 authored Feb 25, 2024
1 parent 1715ed4 commit 0a84917
Show file tree
Hide file tree
Showing 6 changed files with 138 additions and 169 deletions.
13 changes: 9 additions & 4 deletions lib/src/editor/editor_component/service/editor.dart
Original file line number Diff line number Diff line change
Expand Up @@ -182,9 +182,8 @@ class _AppFlowyEditorState extends State<AppFlowyEditor> {
shrinkWrap: widget.shrinkWrap,
);

editorState.editorStyle = widget.editorStyle;
_updateValues();
editorState.renderer = _renderer;
editorState.editable = widget.editable;

// auto focus
WidgetsBinding.instance.addPostFrameCallback((timeStamp) {
Expand All @@ -206,8 +205,7 @@ class _AppFlowyEditorState extends State<AppFlowyEditor> {
void didUpdateWidget(covariant AppFlowyEditor oldWidget) {
super.didUpdateWidget(oldWidget);

editorState.editorStyle = widget.editorStyle;
editorState.editable = widget.editable;
_updateValues();

if (editorState.service != oldWidget.editorState.service) {
editorState.renderer = _renderer;
Expand Down Expand Up @@ -289,6 +287,13 @@ class _AppFlowyEditorState extends State<AppFlowyEditor> {
}
}

void _updateValues() {
editorState.editorStyle = widget.editorStyle;
editorState.editable = widget.editable;
editorState.showHeader = widget.header != null;
editorState.showFooter = widget.footer != null;
}

BlockComponentRendererService get _renderer => BlockComponentRenderer(
builders: {...widget.blockComponentBuilders},
);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -228,7 +228,7 @@ class EditorScrollController {
// Determine the first visible item by finding the item with the
// smallest trailing edge that is greater than 0. i.e. the first
// item whose trailing edge in visible in the viewport.
final min = positions
int min = positions
.where((ItemPosition position) => position.itemTrailingEdge > 0)
.reduce(
(ItemPosition min, ItemPosition position) =>
Expand All @@ -238,15 +238,26 @@ class EditorScrollController {
// Determine the last visible item by finding the item with the
// greatest leading edge that is less than 1. i.e. the last
// item whose leading edge in visible in the viewport.
final max = positions
int max = positions
.where((ItemPosition position) => position.itemLeadingEdge < 1)
.reduce(
(ItemPosition max, ItemPosition position) =>
position.itemLeadingEdge > max.itemLeadingEdge ? position : max,
)
.index;

// filter the header and footer
if (editorState.showHeader) {
max--;
}

if (editorState.showFooter &&
max >= editorState.document.root.children.length) {
max--;
}

// notify the listeners

visibleRangeNotifier.value = (min, max);
}
}
Expand Down
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
import 'dart:math' as math;

import 'package:appflowy_editor/appflowy_editor.dart';
import 'package:appflowy_editor/src/editor/editor_component/service/selection/mobile_selection_service.dart';
import 'package:appflowy_editor/src/editor/editor_component/service/selection/shared.dart';
import 'package:appflowy_editor/src/flutter/overlay.dart';
import 'package:appflowy_editor/src/service/selection/selection_gesture.dart';
import 'package:flutter/material.dart' hide Overlay, OverlayEntry;
Expand Down Expand Up @@ -146,41 +145,18 @@ class _DesktopSelectionServiceWidgetState

@override
Node? getNodeInOffset(Offset offset) {
final List<Node> sortedNodes = getVisibleNodes();
final List<Node> sortedNodes = editorState.getVisibleNodes(
context.read<EditorScrollController>(),
);

return _getNodeInOffset(
return editorState.getNodeInOffset(
sortedNodes,
offset,
0,
sortedNodes.length - 1,
);
}

List<Node> getVisibleNodes() {
final List<Node> sortedNodes = [];
final positions =
context.read<EditorScrollController>().visibleRangeNotifier.value;
// https://github.com/AppFlowy-IO/AppFlowy/issues/3651
final min = math.max(positions.$1 - 1, 0);
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 Down Expand Up @@ -395,76 +371,6 @@ class _DesktopSelectionServiceWidgetState
Overlay.of(context, rootOverlay: true)?.insert(contextMenu);
}

Node? _getNodeInOffset(
List<Node> sortedNodes,
Offset offset,
int start,
int end,
) {
if (start < 0 && end >= sortedNodes.length) {
return null;
}

var min = _findCloseNode(
sortedNodes,
start,
end,
(rect) => rect.bottom <= offset.dy,
);

final filteredNodes = List.of(sortedNodes)
..retainWhere((n) => n.rect.bottom == sortedNodes[min].rect.bottom);
min = 0;
if (filteredNodes.length > 1) {
min = _findCloseNode(
sortedNodes,
0,
filteredNodes.length - 1,
(rect) => rect.right <= offset.dx,
);
}

final node = filteredNodes[min];
if (node.children.isNotEmpty &&
node.children.first.renderBox != null &&
node.children.first.rect.top <= offset.dy) {
final children = node.children.toList(growable: false)
..sort(
(a, b) => a.rect.bottom != b.rect.bottom
? a.rect.bottom.compareTo(b.rect.bottom)
: a.rect.left.compareTo(b.rect.left),
);

return _getNodeInOffset(
children,
offset,
0,
children.length - 1,
);
}
return node;
}

int _findCloseNode(
List<Node> sortedNodes,
int start,
int end,
bool Function(Rect rect) compare,
) {
var min = start;
var max = end;
while (min <= max) {
final mid = min + ((max - min) >> 1);
final rect = sortedNodes[mid].rect;
if (compare(rect)) {
min = mid + 1;
} else {
max = mid - 1;
}
}
return min.clamp(start, end);
}

@override
void registerGestureInterceptor(SelectionGestureInterceptor interceptor) {
_interceptors.add(interceptor);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ import 'dart:io';

import 'package:appflowy_editor/appflowy_editor.dart';
import 'package:appflowy_editor/src/editor/editor_component/service/selection/mobile_magnifier.dart';
import 'package:appflowy_editor/src/editor/editor_component/service/selection/shared.dart';
import 'package:appflowy_editor/src/render/selection/mobile_basic_handle.dart';
import 'package:appflowy_editor/src/render/selection/mobile_collapsed_handle.dart';
import 'package:appflowy_editor/src/render/selection/mobile_selection_handle.dart';
Expand Down Expand Up @@ -326,40 +327,18 @@ class _MobileSelectionServiceWidgetState

@override
Node? getNodeInOffset(Offset offset) {
final List<Node> sortedNodes = getVisibleNodes();
final List<Node> sortedNodes = editorState.getVisibleNodes(
context.read<EditorScrollController>(),
);

return _getNodeInOffset(
return editorState.getNodeInOffset(
sortedNodes,
offset,
0,
sortedNodes.length - 1,
);
}

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 Down Expand Up @@ -627,40 +606,6 @@ class _MobileSelectionServiceWidgetState
}
}

Node? _getNodeInOffset(
List<Node> sortedNodes,
Offset offset,
int start,
int end,
) {
if (start < 0 && end >= sortedNodes.length) {
return null;
}
var min = start;
var max = end;
while (min <= max) {
final mid = min + ((max - min) >> 1);
final rect = sortedNodes[mid].rect;
if (rect.bottom <= offset.dy) {
min = mid + 1;
} else {
max = mid - 1;
}
}
min = min.clamp(start, end);
final node = sortedNodes[min];
if (node.children.isNotEmpty && node.children.first.rect.top <= offset.dy) {
final children = node.children.toList(growable: false);
return _getNodeInOffset(
children,
offset,
0,
children.length - 1,
);
}
return node;
}

bool _isClickOnSelectionArea(Offset point) {
for (final rect in selectionRects) {
if (rect.contains(point)) {
Expand Down
100 changes: 100 additions & 0 deletions lib/src/editor/editor_component/service/selection/shared.dart
Original file line number Diff line number Diff line change
@@ -0,0 +1,100 @@
import 'dart:math' as math;

import 'package:appflowy_editor/appflowy_editor.dart';
import 'package:flutter/material.dart';

extension EditorStateSelection on EditorState {
List<Node> getVisibleNodes(EditorScrollController controller) {
final List<Node> sortedNodes = [];
final positions = controller.visibleRangeNotifier.value;
// https://github.com/AppFlowy-IO/AppFlowy/issues/3651
final min = math.max(positions.$1 - 1, 0);
final max = positions.$2;
if (min < 0 || max < 0) {
return sortedNodes;
}

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

Node? getNodeInOffset(
List<Node> sortedNodes,
Offset offset,
int start,
int end,
) {
if (start < 0 && end >= sortedNodes.length) {
return null;
}

var min = _findCloseNode(
sortedNodes,
start,
end,
(rect) => rect.bottom <= offset.dy,
);

final filteredNodes = List.of(sortedNodes)
..retainWhere((n) => n.rect.bottom == sortedNodes[min].rect.bottom);
min = 0;
if (filteredNodes.length > 1) {
min = _findCloseNode(
sortedNodes,
0,
filteredNodes.length - 1,
(rect) => rect.right <= offset.dx,
);
}

final node = filteredNodes[min];
if (node.children.isNotEmpty &&
node.children.first.renderBox != null &&
node.children.first.rect.top <= offset.dy) {
final children = node.children.toList(growable: false)
..sort(
(a, b) => a.rect.bottom != b.rect.bottom
? a.rect.bottom.compareTo(b.rect.bottom)
: a.rect.left.compareTo(b.rect.left),
);

return getNodeInOffset(
children,
offset,
0,
children.length - 1,
);
}
return node;
}

int _findCloseNode(
List<Node> sortedNodes,
int start,
int end,
bool Function(Rect rect) compare,
) {
var min = start;
var max = end;
while (min <= max) {
final mid = min + ((max - min) >> 1);
final rect = sortedNodes[mid].rect;
if (compare(rect)) {
min = mid + 1;
} else {
max = mid - 1;
}
}
return min.clamp(start, end);
}
}
Loading

0 comments on commit 0a84917

Please sign in to comment.