mirror of
https://github.com/AppFlowy-IO/AppFlowy.git
synced 2024-08-30 18:12:39 +00:00
Merge pull request #1209 from alemoreau/double_tilde_to_strikethrough
feat: double tildes to strikethrough text
This commit is contained in:
commit
e67f91b380
@ -22,21 +22,21 @@ class MessageLookup extends MessageLookupByLibrary {
|
||||
|
||||
final messages = _notInlinedMessages(_notInlinedMessages);
|
||||
static Map<String, Function> _notInlinedMessages(_) => <String, Function>{
|
||||
"bold": MessageLookupByLibrary.simpleMessage(""),
|
||||
"bulletedList": MessageLookupByLibrary.simpleMessage(""),
|
||||
"checkbox": MessageLookupByLibrary.simpleMessage(""),
|
||||
"embedCode": MessageLookupByLibrary.simpleMessage(""),
|
||||
"heading1": MessageLookupByLibrary.simpleMessage(""),
|
||||
"heading2": MessageLookupByLibrary.simpleMessage(""),
|
||||
"heading3": MessageLookupByLibrary.simpleMessage(""),
|
||||
"highlight": MessageLookupByLibrary.simpleMessage(""),
|
||||
"image": MessageLookupByLibrary.simpleMessage(""),
|
||||
"italic": MessageLookupByLibrary.simpleMessage(""),
|
||||
"link": MessageLookupByLibrary.simpleMessage(""),
|
||||
"numberedList": MessageLookupByLibrary.simpleMessage(""),
|
||||
"quote": MessageLookupByLibrary.simpleMessage(""),
|
||||
"strikethrough": MessageLookupByLibrary.simpleMessage(""),
|
||||
"text": MessageLookupByLibrary.simpleMessage(""),
|
||||
"underline": MessageLookupByLibrary.simpleMessage("")
|
||||
"bold": MessageLookupByLibrary.simpleMessage("gras"),
|
||||
"bulletedList": MessageLookupByLibrary.simpleMessage("liste à puces"),
|
||||
"checkbox": MessageLookupByLibrary.simpleMessage("case à cocher"),
|
||||
"embedCode": MessageLookupByLibrary.simpleMessage("incorporer Code"),
|
||||
"heading1": MessageLookupByLibrary.simpleMessage("en-tête1"),
|
||||
"heading2": MessageLookupByLibrary.simpleMessage("en-tête2"),
|
||||
"heading3": MessageLookupByLibrary.simpleMessage("en-tête3"),
|
||||
"highlight": MessageLookupByLibrary.simpleMessage("mettre en évidence"),
|
||||
"image": MessageLookupByLibrary.simpleMessage("l’image"),
|
||||
"italic": MessageLookupByLibrary.simpleMessage("italique"),
|
||||
"link": MessageLookupByLibrary.simpleMessage("lien"),
|
||||
"numberedList": MessageLookupByLibrary.simpleMessage("liste numérotée"),
|
||||
"quote": MessageLookupByLibrary.simpleMessage("citation"),
|
||||
"strikethrough": MessageLookupByLibrary.simpleMessage("barré"),
|
||||
"text": MessageLookupByLibrary.simpleMessage("texte"),
|
||||
"underline": MessageLookupByLibrary.simpleMessage("souligner")
|
||||
};
|
||||
}
|
||||
|
@ -22,21 +22,21 @@ class MessageLookup extends MessageLookupByLibrary {
|
||||
|
||||
final messages = _notInlinedMessages(_notInlinedMessages);
|
||||
static Map<String, Function> _notInlinedMessages(_) => <String, Function>{
|
||||
"bold": MessageLookupByLibrary.simpleMessage(""),
|
||||
"bulletedList": MessageLookupByLibrary.simpleMessage(""),
|
||||
"checkbox": MessageLookupByLibrary.simpleMessage(""),
|
||||
"embedCode": MessageLookupByLibrary.simpleMessage(""),
|
||||
"heading1": MessageLookupByLibrary.simpleMessage(""),
|
||||
"heading2": MessageLookupByLibrary.simpleMessage(""),
|
||||
"heading3": MessageLookupByLibrary.simpleMessage(""),
|
||||
"highlight": MessageLookupByLibrary.simpleMessage(""),
|
||||
"image": MessageLookupByLibrary.simpleMessage(""),
|
||||
"italic": MessageLookupByLibrary.simpleMessage(""),
|
||||
"link": MessageLookupByLibrary.simpleMessage(""),
|
||||
"numberedList": MessageLookupByLibrary.simpleMessage(""),
|
||||
"quote": MessageLookupByLibrary.simpleMessage(""),
|
||||
"strikethrough": MessageLookupByLibrary.simpleMessage(""),
|
||||
"text": MessageLookupByLibrary.simpleMessage(""),
|
||||
"underline": MessageLookupByLibrary.simpleMessage("")
|
||||
"bold": MessageLookupByLibrary.simpleMessage("bátor"),
|
||||
"bulletedList": MessageLookupByLibrary.simpleMessage("pontozott lista"),
|
||||
"checkbox": MessageLookupByLibrary.simpleMessage("jelölőnégyzetet"),
|
||||
"embedCode": MessageLookupByLibrary.simpleMessage("Beágyazás"),
|
||||
"heading1": MessageLookupByLibrary.simpleMessage("címsor1"),
|
||||
"heading2": MessageLookupByLibrary.simpleMessage("címsor2"),
|
||||
"heading3": MessageLookupByLibrary.simpleMessage("címsor3"),
|
||||
"highlight": MessageLookupByLibrary.simpleMessage("Kiemel"),
|
||||
"image": MessageLookupByLibrary.simpleMessage("kép"),
|
||||
"italic": MessageLookupByLibrary.simpleMessage("dőlt"),
|
||||
"link": MessageLookupByLibrary.simpleMessage("link"),
|
||||
"numberedList": MessageLookupByLibrary.simpleMessage("számozottLista"),
|
||||
"quote": MessageLookupByLibrary.simpleMessage("idézet"),
|
||||
"strikethrough": MessageLookupByLibrary.simpleMessage("áthúzott"),
|
||||
"text": MessageLookupByLibrary.simpleMessage("szöveg"),
|
||||
"underline": MessageLookupByLibrary.simpleMessage("aláhúzás")
|
||||
};
|
||||
}
|
||||
|
@ -22,21 +22,21 @@ class MessageLookup extends MessageLookupByLibrary {
|
||||
|
||||
final messages = _notInlinedMessages(_notInlinedMessages);
|
||||
static Map<String, Function> _notInlinedMessages(_) => <String, Function>{
|
||||
"bold": MessageLookupByLibrary.simpleMessage(""),
|
||||
"bulletedList": MessageLookupByLibrary.simpleMessage(""),
|
||||
"checkbox": MessageLookupByLibrary.simpleMessage(""),
|
||||
"embedCode": MessageLookupByLibrary.simpleMessage(""),
|
||||
"heading1": MessageLookupByLibrary.simpleMessage(""),
|
||||
"heading2": MessageLookupByLibrary.simpleMessage(""),
|
||||
"heading3": MessageLookupByLibrary.simpleMessage(""),
|
||||
"highlight": MessageLookupByLibrary.simpleMessage(""),
|
||||
"image": MessageLookupByLibrary.simpleMessage(""),
|
||||
"italic": MessageLookupByLibrary.simpleMessage(""),
|
||||
"link": MessageLookupByLibrary.simpleMessage(""),
|
||||
"numberedList": MessageLookupByLibrary.simpleMessage(""),
|
||||
"quote": MessageLookupByLibrary.simpleMessage(""),
|
||||
"strikethrough": MessageLookupByLibrary.simpleMessage(""),
|
||||
"text": MessageLookupByLibrary.simpleMessage(""),
|
||||
"underline": MessageLookupByLibrary.simpleMessage("")
|
||||
"bold": MessageLookupByLibrary.simpleMessage("berani"),
|
||||
"bulletedList": MessageLookupByLibrary.simpleMessage("daftar berpoin"),
|
||||
"checkbox": MessageLookupByLibrary.simpleMessage("kotak centang"),
|
||||
"embedCode": MessageLookupByLibrary.simpleMessage("menyematkan Kode"),
|
||||
"heading1": MessageLookupByLibrary.simpleMessage("pos1"),
|
||||
"heading2": MessageLookupByLibrary.simpleMessage("pos2"),
|
||||
"heading3": MessageLookupByLibrary.simpleMessage("pos3"),
|
||||
"highlight": MessageLookupByLibrary.simpleMessage("menyorot"),
|
||||
"image": MessageLookupByLibrary.simpleMessage("gambar"),
|
||||
"italic": MessageLookupByLibrary.simpleMessage("miring"),
|
||||
"link": MessageLookupByLibrary.simpleMessage("tautan"),
|
||||
"numberedList": MessageLookupByLibrary.simpleMessage("daftar bernomor"),
|
||||
"quote": MessageLookupByLibrary.simpleMessage("mengutip"),
|
||||
"strikethrough": MessageLookupByLibrary.simpleMessage("coret"),
|
||||
"text": MessageLookupByLibrary.simpleMessage("teks"),
|
||||
"underline": MessageLookupByLibrary.simpleMessage("menggarisbawahi")
|
||||
};
|
||||
}
|
||||
|
@ -22,21 +22,22 @@ class MessageLookup extends MessageLookupByLibrary {
|
||||
|
||||
final messages = _notInlinedMessages(_notInlinedMessages);
|
||||
static Map<String, Function> _notInlinedMessages(_) => <String, Function>{
|
||||
"bold": MessageLookupByLibrary.simpleMessage(""),
|
||||
"bulletedList": MessageLookupByLibrary.simpleMessage(""),
|
||||
"checkbox": MessageLookupByLibrary.simpleMessage(""),
|
||||
"embedCode": MessageLookupByLibrary.simpleMessage(""),
|
||||
"heading1": MessageLookupByLibrary.simpleMessage(""),
|
||||
"heading2": MessageLookupByLibrary.simpleMessage(""),
|
||||
"heading3": MessageLookupByLibrary.simpleMessage(""),
|
||||
"highlight": MessageLookupByLibrary.simpleMessage(""),
|
||||
"image": MessageLookupByLibrary.simpleMessage(""),
|
||||
"italic": MessageLookupByLibrary.simpleMessage(""),
|
||||
"link": MessageLookupByLibrary.simpleMessage(""),
|
||||
"numberedList": MessageLookupByLibrary.simpleMessage(""),
|
||||
"quote": MessageLookupByLibrary.simpleMessage(""),
|
||||
"strikethrough": MessageLookupByLibrary.simpleMessage(""),
|
||||
"text": MessageLookupByLibrary.simpleMessage(""),
|
||||
"underline": MessageLookupByLibrary.simpleMessage("")
|
||||
"bold": MessageLookupByLibrary.simpleMessage("negrito"),
|
||||
"bulletedList":
|
||||
MessageLookupByLibrary.simpleMessage("lista com marcadores"),
|
||||
"checkbox": MessageLookupByLibrary.simpleMessage("caixa de seleção"),
|
||||
"embedCode": MessageLookupByLibrary.simpleMessage("Código embutido"),
|
||||
"heading1": MessageLookupByLibrary.simpleMessage("Cabeçallho 1"),
|
||||
"heading2": MessageLookupByLibrary.simpleMessage("Cabeçallho 2"),
|
||||
"heading3": MessageLookupByLibrary.simpleMessage("Cabeçallho 3"),
|
||||
"highlight": MessageLookupByLibrary.simpleMessage("realçar"),
|
||||
"image": MessageLookupByLibrary.simpleMessage("imagem"),
|
||||
"italic": MessageLookupByLibrary.simpleMessage("itálico"),
|
||||
"link": MessageLookupByLibrary.simpleMessage("link"),
|
||||
"numberedList": MessageLookupByLibrary.simpleMessage("lista numerada"),
|
||||
"quote": MessageLookupByLibrary.simpleMessage("citar"),
|
||||
"strikethrough": MessageLookupByLibrary.simpleMessage("tachado"),
|
||||
"text": MessageLookupByLibrary.simpleMessage("texto"),
|
||||
"underline": MessageLookupByLibrary.simpleMessage("sublinhado")
|
||||
};
|
||||
}
|
||||
|
@ -1,4 +1,3 @@
|
||||
import "dart:math";
|
||||
import 'package:appflowy_editor/appflowy_editor.dart';
|
||||
import 'package:appflowy_editor/src/extensions/text_node_extensions.dart';
|
||||
import 'package:appflowy_editor/src/service/default_text_operations/format_rich_text_style.dart';
|
||||
@ -49,7 +48,7 @@ ShortcutEventHandler backquoteToCodeHandler = (editorState, event) {
|
||||
.substring(selection.start.offset, selection.end.offset);
|
||||
|
||||
// toggle code style when selected some text
|
||||
if (selectionText.length > 0) {
|
||||
if (selectionText.isNotEmpty) {
|
||||
formatEmbedCode(editorState);
|
||||
return KeyEventResult.handled;
|
||||
}
|
||||
@ -125,6 +124,69 @@ ShortcutEventHandler backquoteToCodeHandler = (editorState, event) {
|
||||
return KeyEventResult.handled;
|
||||
};
|
||||
|
||||
// convert ~~abc~~ to strikethrough abc.
|
||||
ShortcutEventHandler doubleTildeToStrikethrough = (editorState, event) {
|
||||
final selectionService = editorState.service.selectionService;
|
||||
final selection = selectionService.currentSelection.value;
|
||||
final textNodes = selectionService.currentSelectedNodes.whereType<TextNode>();
|
||||
if (selection == null || !selection.isSingle || textNodes.length != 1) {
|
||||
return KeyEventResult.ignored;
|
||||
}
|
||||
|
||||
final textNode = textNodes.first;
|
||||
final text = textNode.toRawString().substring(0, selection.end.offset);
|
||||
|
||||
// make sure the last two characters are ~~.
|
||||
if (text.length < 2 || text[selection.end.offset - 1] != '~') {
|
||||
return KeyEventResult.ignored;
|
||||
}
|
||||
|
||||
// find all the index of `~`.
|
||||
final tildeIndexes = <int>[];
|
||||
for (var i = 0; i < text.length; i++) {
|
||||
if (text[i] == '~') {
|
||||
tildeIndexes.add(i);
|
||||
}
|
||||
}
|
||||
|
||||
if (tildeIndexes.length < 3) {
|
||||
return KeyEventResult.ignored;
|
||||
}
|
||||
|
||||
// make sure the second to last and third to last tildes are connected.
|
||||
final thirdToLastTildeIndex = tildeIndexes[tildeIndexes.length - 3];
|
||||
final secondToLastTildeIndex = tildeIndexes[tildeIndexes.length - 2];
|
||||
final lastTildeIndex = tildeIndexes[tildeIndexes.length - 1];
|
||||
if (secondToLastTildeIndex != thirdToLastTildeIndex + 1 ||
|
||||
lastTildeIndex == secondToLastTildeIndex + 1) {
|
||||
return KeyEventResult.ignored;
|
||||
}
|
||||
|
||||
// delete the last three tildes.
|
||||
// update the style of the text surround by `~~ ~~` to strikethrough.
|
||||
// and update the cursor position.
|
||||
TransactionBuilder(editorState)
|
||||
..deleteText(textNode, lastTildeIndex, 1)
|
||||
..deleteText(textNode, thirdToLastTildeIndex, 2)
|
||||
..formatText(
|
||||
textNode,
|
||||
thirdToLastTildeIndex,
|
||||
selection.end.offset - thirdToLastTildeIndex - 2,
|
||||
{
|
||||
BuiltInAttributeKey.strikethrough: true,
|
||||
},
|
||||
)
|
||||
..afterSelection = Selection.collapsed(
|
||||
Position(
|
||||
path: textNode.path,
|
||||
offset: selection.end.offset - 3,
|
||||
),
|
||||
)
|
||||
..commit();
|
||||
|
||||
return KeyEventResult.handled;
|
||||
};
|
||||
|
||||
/// To create a link, enclose the link text in brackets (e.g., [link text]).
|
||||
/// Then, immediately follow it with the URL in parentheses (e.g., (https://example.com)).
|
||||
ShortcutEventHandler markdownLinkToLinkHandler = (editorState, event) {
|
||||
|
@ -257,6 +257,11 @@ List<ShortcutEvent> builtInShortcutEvents = [
|
||||
command: 'backquote',
|
||||
handler: backquoteToCodeHandler,
|
||||
),
|
||||
ShortcutEvent(
|
||||
key: 'Double tilde to strikethrough',
|
||||
command: 'shift+tilde',
|
||||
handler: doubleTildeToStrikethrough,
|
||||
),
|
||||
ShortcutEvent(
|
||||
key: 'Markdown link to link',
|
||||
command: 'shift+parenthesis right',
|
||||
|
@ -139,6 +139,9 @@ extension on LogicalKeyboardKey {
|
||||
if (this == LogicalKeyboardKey.keyZ) {
|
||||
return PhysicalKeyboardKey.keyZ;
|
||||
}
|
||||
if (this == LogicalKeyboardKey.tilde) {
|
||||
return PhysicalKeyboardKey.backquote;
|
||||
}
|
||||
throw UnimplementedError();
|
||||
}
|
||||
}
|
||||
|
@ -150,5 +150,111 @@ void main() async {
|
||||
expect(textNode.toRawString(), text);
|
||||
});
|
||||
});
|
||||
|
||||
group('convert double tilde to strikethrough', () {
|
||||
Future<void> insertTilde(
|
||||
EditorWidgetTester editor, {
|
||||
int repeat = 1,
|
||||
}) async {
|
||||
for (var i = 0; i < repeat; i++) {
|
||||
await editor.pressLogicKey(
|
||||
LogicalKeyboardKey.tilde,
|
||||
isShiftPressed: true,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
testWidgets('~~AppFlowy~~ to strikethrough AppFlowy', (tester) async {
|
||||
const text = '~~AppFlowy~';
|
||||
final editor = tester.editor..insertTextNode('');
|
||||
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.insertText(textNode, text[i], i);
|
||||
}
|
||||
await insertTilde(editor);
|
||||
final allStrikethrough = textNode.allSatisfyStrikethroughInSelection(
|
||||
Selection.single(
|
||||
path: [0],
|
||||
startOffset: 0,
|
||||
endOffset: textNode.toRawString().length,
|
||||
),
|
||||
);
|
||||
expect(allStrikethrough, true);
|
||||
expect(textNode.toRawString(), 'AppFlowy');
|
||||
});
|
||||
|
||||
testWidgets('App~~Flowy~~ to strikethrough AppFlowy', (tester) async {
|
||||
const text = 'App~~Flowy~';
|
||||
final editor = tester.editor..insertTextNode('');
|
||||
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.insertText(textNode, text[i], i);
|
||||
}
|
||||
await insertTilde(editor);
|
||||
final allStrikethrough = textNode.allSatisfyStrikethroughInSelection(
|
||||
Selection.single(
|
||||
path: [0],
|
||||
startOffset: 3,
|
||||
endOffset: textNode.toRawString().length,
|
||||
),
|
||||
);
|
||||
expect(allStrikethrough, true);
|
||||
expect(textNode.toRawString(), 'AppFlowy');
|
||||
});
|
||||
|
||||
testWidgets('~~~AppFlowy~~ to bold ~AppFlowy', (tester) async {
|
||||
const text = '~~~AppFlowy~';
|
||||
final editor = tester.editor..insertTextNode('');
|
||||
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.insertText(textNode, text[i], i);
|
||||
}
|
||||
await insertTilde(editor);
|
||||
final allStrikethrough = textNode.allSatisfyStrikethroughInSelection(
|
||||
Selection.single(
|
||||
path: [0],
|
||||
startOffset: 1,
|
||||
endOffset: textNode.toRawString().length,
|
||||
),
|
||||
);
|
||||
expect(allStrikethrough, true);
|
||||
expect(textNode.toRawString(), '~AppFlowy');
|
||||
});
|
||||
|
||||
testWidgets('~~~~ nothing changes', (tester) async {
|
||||
const text = '~~~';
|
||||
final editor = tester.editor..insertTextNode('');
|
||||
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.insertText(textNode, text[i], i);
|
||||
}
|
||||
await insertTilde(editor);
|
||||
final allStrikethrough = textNode.allSatisfyStrikethroughInSelection(
|
||||
Selection.single(
|
||||
path: [0],
|
||||
startOffset: 0,
|
||||
endOffset: textNode.toRawString().length,
|
||||
),
|
||||
);
|
||||
expect(allStrikethrough, false);
|
||||
expect(textNode.toRawString(), text);
|
||||
});
|
||||
});
|
||||
});
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user