mirror of
https://github.com/AppFlowy-IO/AppFlowy.git
synced 2024-08-30 18:12:39 +00:00
fix: selection is wrong after pressing arrow left / right
This commit is contained in:
parent
28d9b8ee3a
commit
45a120fe88
@ -5,18 +5,23 @@ class FlowySvg extends StatelessWidget {
|
||||
const FlowySvg({
|
||||
Key? key,
|
||||
this.name,
|
||||
this.size = const Size(20, 20),
|
||||
this.width,
|
||||
this.height,
|
||||
this.color,
|
||||
this.number,
|
||||
this.padding,
|
||||
}) : super(key: key);
|
||||
|
||||
final String? name;
|
||||
final Size size;
|
||||
final double? width;
|
||||
final double? height;
|
||||
final Color? color;
|
||||
final int? number;
|
||||
final EdgeInsets? padding;
|
||||
|
||||
final _defaultWidth = 20.0;
|
||||
final _defaultHeight = 20.0;
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return Padding(
|
||||
@ -27,22 +32,21 @@ class FlowySvg extends StatelessWidget {
|
||||
|
||||
Widget _buildSvg() {
|
||||
if (name != null) {
|
||||
return SizedBox.fromSize(
|
||||
size: size,
|
||||
child: SvgPicture.asset(
|
||||
'assets/images/$name.svg',
|
||||
color: color,
|
||||
package: 'appflowy_editor',
|
||||
fit: BoxFit.fill,
|
||||
),
|
||||
return SvgPicture.asset(
|
||||
'assets/images/$name.svg',
|
||||
color: color,
|
||||
fit: BoxFit.fill,
|
||||
height: height,
|
||||
width: width,
|
||||
package: 'appflowy_editor',
|
||||
);
|
||||
} else if (number != null) {
|
||||
final numberText =
|
||||
'<svg width="200" height="200" xmlns="http://www.w3.org/2000/svg"><text x="30" y="150" fill="black" font-size="160">$number.</text></svg>';
|
||||
return SvgPicture.string(
|
||||
numberText,
|
||||
width: size.width,
|
||||
height: size.width,
|
||||
width: width ?? _defaultWidth,
|
||||
height: height ?? _defaultHeight,
|
||||
);
|
||||
}
|
||||
return Container();
|
||||
|
@ -47,7 +47,7 @@ class _BulletedListTextNodeWidgetState extends State<BulletedListTextNodeWidget>
|
||||
final iconKey = GlobalKey();
|
||||
|
||||
final _richTextKey = GlobalKey(debugLabel: 'bulleted_list_text');
|
||||
final _iconSize = 20.0;
|
||||
final _iconWidth = 20.0;
|
||||
final _iconRightPadding = 5.0;
|
||||
|
||||
@override
|
||||
@ -67,7 +67,8 @@ class _BulletedListTextNodeWidgetState extends State<BulletedListTextNodeWidget>
|
||||
children: [
|
||||
FlowySvg(
|
||||
key: iconKey,
|
||||
size: Size.square(_iconSize),
|
||||
width: _iconWidth,
|
||||
height: _iconWidth,
|
||||
padding:
|
||||
EdgeInsets.only(top: topPadding, right: _iconRightPadding),
|
||||
name: 'point',
|
||||
|
@ -45,7 +45,7 @@ class _CheckboxNodeWidgetState extends State<CheckboxNodeWidget>
|
||||
final iconKey = GlobalKey();
|
||||
|
||||
final _richTextKey = GlobalKey(debugLabel: 'checkbox_text');
|
||||
final _iconSize = 20.0;
|
||||
final _iconWidth = 20.0;
|
||||
final _iconRightPadding = 5.0;
|
||||
|
||||
@override
|
||||
@ -74,7 +74,8 @@ class _CheckboxNodeWidgetState extends State<CheckboxNodeWidget>
|
||||
GestureDetector(
|
||||
key: iconKey,
|
||||
child: FlowySvg(
|
||||
size: Size.square(_iconSize),
|
||||
width: _iconWidth,
|
||||
height: _iconWidth,
|
||||
padding: EdgeInsets.only(
|
||||
top: topPadding,
|
||||
right: _iconRightPadding,
|
||||
|
@ -47,7 +47,7 @@ class _NumberListTextNodeWidgetState extends State<NumberListTextNodeWidget>
|
||||
final iconKey = GlobalKey();
|
||||
|
||||
final _richTextKey = GlobalKey(debugLabel: 'number_list_text');
|
||||
final _iconSize = 20.0;
|
||||
final _iconWidth = 20.0;
|
||||
final _iconRightPadding = 5.0;
|
||||
|
||||
@override
|
||||
@ -66,7 +66,8 @@ class _NumberListTextNodeWidgetState extends State<NumberListTextNodeWidget>
|
||||
children: [
|
||||
FlowySvg(
|
||||
key: iconKey,
|
||||
size: Size.square(_iconSize),
|
||||
width: _iconWidth,
|
||||
height: _iconWidth,
|
||||
padding:
|
||||
EdgeInsets.only(top: topPadding, right: _iconRightPadding),
|
||||
number: widget.textNode.attributes.number,
|
||||
|
@ -46,7 +46,7 @@ class _QuotedTextNodeWidgetState extends State<QuotedTextNodeWidget>
|
||||
final iconKey = GlobalKey();
|
||||
|
||||
final _richTextKey = GlobalKey(debugLabel: 'quoted_text');
|
||||
final _iconSize = 20.0;
|
||||
final _iconWidth = 20.0;
|
||||
final _iconRightPadding = 5.0;
|
||||
|
||||
@override
|
||||
@ -60,25 +60,27 @@ class _QuotedTextNodeWidgetState extends State<QuotedTextNodeWidget>
|
||||
width: defaultMaxTextNodeWidth,
|
||||
child: Padding(
|
||||
padding: EdgeInsets.only(bottom: defaultLinePadding),
|
||||
child: Row(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
FlowySvg(
|
||||
key: iconKey,
|
||||
size: Size(_iconSize, _quoteHeight),
|
||||
padding:
|
||||
EdgeInsets.only(top: topPadding, right: _iconRightPadding),
|
||||
name: 'quote',
|
||||
),
|
||||
Expanded(
|
||||
child: FlowyRichText(
|
||||
key: _richTextKey,
|
||||
placeholderText: 'Quote',
|
||||
textNode: widget.textNode,
|
||||
editorState: widget.editorState,
|
||||
child: IntrinsicHeight(
|
||||
child: Row(
|
||||
crossAxisAlignment: CrossAxisAlignment.stretch,
|
||||
children: [
|
||||
FlowySvg(
|
||||
key: iconKey,
|
||||
width: _iconWidth,
|
||||
padding: EdgeInsets.only(
|
||||
top: topPadding, right: _iconRightPadding),
|
||||
name: 'quote',
|
||||
),
|
||||
),
|
||||
],
|
||||
Expanded(
|
||||
child: FlowyRichText(
|
||||
key: _richTextKey,
|
||||
placeholderText: 'Quote',
|
||||
textNode: widget.textNode,
|
||||
editorState: widget.editorState,
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
));
|
||||
}
|
||||
@ -86,6 +88,6 @@ class _QuotedTextNodeWidgetState extends State<QuotedTextNodeWidget>
|
||||
double get _quoteHeight {
|
||||
final lines =
|
||||
widget.textNode.toRawString().characters.where((c) => c == '\n').length;
|
||||
return (lines + 1) * _iconSize;
|
||||
return (lines + 1) * _iconWidth;
|
||||
}
|
||||
}
|
||||
|
@ -141,7 +141,7 @@ class _ToolbarWidgetState extends State<ToolbarWidget> with ToolbarMixin {
|
||||
Size(toolbarHeight - (width != null ? 20 : 0), toolbarHeight),
|
||||
child: Center(
|
||||
child: FlowySvg(
|
||||
size: Size(width ?? 20, 20),
|
||||
width: width ?? 20,
|
||||
name: 'toolbar/$name',
|
||||
),
|
||||
),
|
||||
|
@ -120,8 +120,9 @@ AppFlowyKeyEventHandler arrowKeysHandler = (editorState, event) {
|
||||
editorState.updateCursorSelection(Selection.collapsed(leftPosition));
|
||||
}
|
||||
} else {
|
||||
editorState
|
||||
.updateCursorSelection(currentSelection.collapse(atStart: true));
|
||||
editorState.updateCursorSelection(
|
||||
currentSelection.collapse(atStart: currentSelection.isBackward),
|
||||
);
|
||||
}
|
||||
return KeyEventResult.handled;
|
||||
} else if (event.logicalKey == LogicalKeyboardKey.arrowRight) {
|
||||
@ -131,7 +132,9 @@ AppFlowyKeyEventHandler arrowKeysHandler = (editorState, event) {
|
||||
editorState.updateCursorSelection(Selection.collapsed(rightPosition));
|
||||
}
|
||||
} else {
|
||||
editorState.updateCursorSelection(currentSelection.collapse());
|
||||
editorState.updateCursorSelection(
|
||||
currentSelection.collapse(atStart: !currentSelection.isBackward),
|
||||
);
|
||||
}
|
||||
return KeyEventResult.handled;
|
||||
} else if (event.logicalKey == LogicalKeyboardKey.arrowUp) {
|
||||
|
@ -448,5 +448,6 @@ class PopupListItem {
|
||||
Widget _popupListIcon(String name) => FlowySvg(
|
||||
name: 'popup_list/$name',
|
||||
color: Colors.black,
|
||||
size: const Size.square(18.0),
|
||||
width: 18.0,
|
||||
height: 18.0,
|
||||
);
|
||||
|
@ -88,6 +88,12 @@ extension on LogicalKeyboardKey {
|
||||
if (this == LogicalKeyboardKey.delete) {
|
||||
return PhysicalKeyboardKey.delete;
|
||||
}
|
||||
if (this == LogicalKeyboardKey.arrowRight) {
|
||||
return PhysicalKeyboardKey.arrowRight;
|
||||
}
|
||||
if (this == LogicalKeyboardKey.arrowLeft) {
|
||||
return PhysicalKeyboardKey.arrowLeft;
|
||||
}
|
||||
if (this == LogicalKeyboardKey.pageDown) {
|
||||
return PhysicalKeyboardKey.pageDown;
|
||||
}
|
||||
|
@ -0,0 +1,84 @@
|
||||
import 'package:appflowy_editor/appflowy_editor.dart';
|
||||
import 'package:flutter/services.dart';
|
||||
import 'package:flutter_test/flutter_test.dart';
|
||||
import '../../infra/test_editor.dart';
|
||||
|
||||
void main() async {
|
||||
setUpAll(() {
|
||||
TestWidgetsFlutterBinding.ensureInitialized();
|
||||
});
|
||||
|
||||
group('arrow_keys_handler.dart', () {
|
||||
testWidgets('Presses arrow right key, move the cursor from left to right',
|
||||
(tester) async {
|
||||
const text = 'Welcome to Appflowy 😁';
|
||||
final editor = tester.editor
|
||||
..insertTextNode(text)
|
||||
..insertTextNode(text);
|
||||
await editor.startTesting();
|
||||
|
||||
await editor.updateSelection(
|
||||
Selection.single(path: [0], startOffset: 0),
|
||||
);
|
||||
|
||||
final textNode = editor.nodeAtPath([0]) as TextNode;
|
||||
for (var i = 0; i < text.length; i++) {
|
||||
await editor.pressLogicKey(LogicalKeyboardKey.arrowRight);
|
||||
|
||||
if (i == text.length - 1) {
|
||||
// Wrap to next node if the cursor is at the end of the current node.
|
||||
expect(
|
||||
editor.documentSelection,
|
||||
Selection.single(
|
||||
path: [1],
|
||||
startOffset: 0,
|
||||
),
|
||||
);
|
||||
} else {
|
||||
expect(
|
||||
editor.documentSelection,
|
||||
Selection.single(
|
||||
path: [0],
|
||||
startOffset: textNode.delta.nextRunePosition(i),
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
testWidgets(
|
||||
'Presses arrow left/right key since selection is not collapsed and backward',
|
||||
(tester) async {
|
||||
await _testPressArrowKeyInNotCollapsedSelection(tester, true);
|
||||
});
|
||||
|
||||
testWidgets(
|
||||
'Presses arrow left/right key since selection is not collapsed and forward',
|
||||
(tester) async {
|
||||
await _testPressArrowKeyInNotCollapsedSelection(tester, false);
|
||||
});
|
||||
}
|
||||
|
||||
Future<void> _testPressArrowKeyInNotCollapsedSelection(
|
||||
WidgetTester tester, bool isBackward) async {
|
||||
const text = 'Welcome to Appflowy 😁';
|
||||
final editor = tester.editor
|
||||
..insertTextNode(text)
|
||||
..insertTextNode(text);
|
||||
await editor.startTesting();
|
||||
|
||||
final start = Position(path: [0], offset: 5);
|
||||
final end = Position(path: [1], offset: 10);
|
||||
final selection = Selection(
|
||||
start: isBackward ? start : end,
|
||||
end: isBackward ? end : start,
|
||||
);
|
||||
await editor.updateSelection(selection);
|
||||
await editor.pressLogicKey(LogicalKeyboardKey.arrowLeft);
|
||||
expect(editor.documentSelection?.start, start);
|
||||
|
||||
await editor.updateSelection(selection);
|
||||
await editor.pressLogicKey(LogicalKeyboardKey.arrowRight);
|
||||
expect(editor.documentSelection?.end, end);
|
||||
}
|
Loading…
Reference in New Issue
Block a user