mirror of
https://github.com/AppFlowy-IO/AppFlowy.git
synced 2024-08-30 18:12:39 +00:00
feat: optimize selection implement by binary search
This commit is contained in:
parent
25226bf26b
commit
4b4ce1020d
File diff suppressed because it is too large
Load Diff
@ -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": [
|
||||
|
@ -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(),
|
||||
},
|
||||
),
|
||||
);
|
||||
}
|
||||
},
|
||||
);
|
||||
}
|
||||
}
|
||||
|
@ -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
|
||||
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
@ -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]
|
||||
|
Loading…
Reference in New Issue
Block a user