feat: optimize selection implement by binary search

This commit is contained in:
Lucas.Xu 2022-08-05 23:59:24 +08:00
parent 25226bf26b
commit 4b4ce1020d
6 changed files with 42053 additions and 12 deletions

File diff suppressed because it is too large Load Diff

View File

@ -9,12 +9,6 @@
"image_src": "https://s1.ax1x.com/2022/07/28/vCgz1x.png"
}
},
{
"type": "youtube_link",
"attributes": {
"youtube_link": "https://flutter.github.io/assets-for-api-docs/assets/videos/bee.mp4"
}
},
{
"type": "text",
"delta": [

View File

@ -99,6 +99,15 @@ class _MyHomePageState extends State<MyHomePage> {
},
icon: const Icon(Icons.text_fields),
),
ActionButton(
onPressed: () {
if (page == 3) return;
setState(() {
page = 3;
});
},
icon: const Icon(Icons.email),
),
],
),
);
@ -111,6 +120,8 @@ class _MyHomePageState extends State<MyHomePage> {
return _buildFlowyEditorWithEmptyDocument();
} else if (page == 2) {
return _buildTextField();
} else if (page == 3) {
return _buildFlowyEditorWithBigDocument();
}
return Container();
}
@ -205,4 +216,34 @@ class _MyHomePageState extends State<MyHomePage> {
child: TextField(),
);
}
Widget _buildFlowyEditorWithBigDocument() {
return FutureBuilder<String>(
future: rootBundle.loadString('assets/big_document.json'),
builder: (context, snapshot) {
if (!snapshot.hasData) {
return const Center(
child: CircularProgressIndicator(),
);
} else {
final data = Map<String, Object>.from(json.decode(snapshot.data!));
final document = StateTree.fromJson(data);
_editorState = EditorState(
document: document,
);
return Container(
padding: const EdgeInsets.only(left: 20, right: 20),
child: FlowyEditor(
key: editorKey,
editorState: _editorState,
keyEventHandlers: const [],
customBuilders: {
'image': ImageNodeBuilder(),
},
),
);
}
},
);
}
}

View File

@ -68,6 +68,7 @@ flutter:
assets:
- document.json
- example.json
- big_document.json
# - images/a_dot_ham.jpeg
# An image asset can refer to one or more resolution-specific "variants", see

View File

@ -19,4 +19,12 @@ extension NodeExtensions on Node {
return selection.end.path <= path && path <= selection.start.path;
}
}
Rect get rect {
if (renderBox != null) {
final boxOffset = renderBox!.localToGlobal(Offset.zero);
return boxOffset & renderBox!.size;
}
return Rect.zero;
}
}

View File

@ -359,12 +359,14 @@ class _FlowySelectionState extends State<FlowySelection>
panStartOffsetWithScrollDyGap =
panStartOffsetWithScrollDyGap.translate(0, panStartScrollDy! - dy);
}
final nodes = getNodesInRange(panStartOffsetWithScrollDyGap, panEndOffset!);
if (nodes.isEmpty) {
return;
}
final first = nodes.first.selectable;
final last = nodes.last.selectable;
final sortedNodes =
editorState.document.root.children.toList(growable: false);
final first = _lowerBound(
sortedNodes, panStartOffsetWithScrollDyGap, 0, sortedNodes.length)
.selectable;
final last = _upperBound(sortedNodes, panEndOffset!, 0, sortedNodes.length)
.selectable;
// compute the selection in range.
if (first != null && last != null) {
@ -559,6 +561,41 @@ class _FlowySelectionState extends State<FlowySelection>
}
}
}
// find the first node's rect.top <= offset.dy
Node _lowerBound(List<Node> sortedNodes, Offset offset, int start, int end) {
var min = start;
var max = end;
while (min <= max) {
final mid = min + ((max - min) >> 1);
if (sortedNodes[mid].rect.bottom <= offset.dy) {
min = mid + 1;
} else {
max = mid - 1;
}
}
return sortedNodes[min];
}
// find the first node's rect.top < offset.dy
Node _upperBound(
List<Node> sortedNodes,
Offset offset,
int start,
int end,
) {
var min = start;
var max = end;
while (min <= max) {
final mid = min + ((max - min) >> 1);
if (sortedNodes[mid].rect.top < offset.dy) {
min = mid + 1;
} else {
max = mid - 1;
}
}
return sortedNodes[max];
}
}
/// Because the flutter's [DoubleTapGestureRecognizer] will block the [TapGestureRecognizer]