fix: link text is only displayed if all selected text satisfy linked style

This commit is contained in:
Lucas.Xu 2022-08-23 10:30:56 +08:00
parent 44b4ef4ad7
commit 60a7557520
6 changed files with 63 additions and 20 deletions

View File

@ -29,24 +29,34 @@ extension TextNodeExtension on TextNode {
}
bool allSatisfyLinkInSelection(Selection selection) =>
allSatisfyInSelection(StyleKey.href, null, selection);
allSatisfyInSelection(StyleKey.href, selection, (value) {
return value != null;
});
bool allSatisfyBoldInSelection(Selection selection) =>
allSatisfyInSelection(StyleKey.bold, true, selection);
allSatisfyInSelection(StyleKey.bold, selection, (value) {
return value == true;
});
bool allSatisfyItalicInSelection(Selection selection) =>
allSatisfyInSelection(StyleKey.italic, true, selection);
allSatisfyInSelection(StyleKey.italic, selection, (value) {
return value == true;
});
bool allSatisfyUnderlineInSelection(Selection selection) =>
allSatisfyInSelection(StyleKey.underline, true, selection);
allSatisfyInSelection(StyleKey.underline, selection, (value) {
return value == true;
});
bool allSatisfyStrikethroughInSelection(Selection selection) =>
allSatisfyInSelection(StyleKey.strikethrough, true, selection);
allSatisfyInSelection(StyleKey.strikethrough, selection, (value) {
return value == true;
});
bool allSatisfyInSelection(
String styleKey,
dynamic value,
Selection selection,
bool Function(dynamic value) compare,
) {
final ops = delta.whereType<TextInsert>();
final startOffset =
@ -62,7 +72,7 @@ extension TextNodeExtension on TextNode {
if (start < endOffset && start + length > startOffset) {
if (op.attributes == null ||
!op.attributes!.containsKey(styleKey) ||
op.attributes![styleKey] != value) {
!compare(op.attributes![styleKey])) {
return false;
}
}
@ -116,13 +126,15 @@ extension TextNodesExtension on List<TextNode> {
bool allSatisfyInSelection(
String styleKey,
Selection selection,
dynamic value,
dynamic matchValue,
) {
if (isEmpty) {
return false;
}
if (length == 1) {
return first.allSatisfyInSelection(styleKey, value, selection);
return first.allSatisfyInSelection(styleKey, selection, (value) {
return value == matchValue;
});
} else {
for (var i = 0; i < length; i++) {
final node = this[i];
@ -142,7 +154,9 @@ extension TextNodesExtension on List<TextNode> {
end: Position(path: node.path, offset: node.toRawString().length),
);
}
if (!node.allSatisfyInSelection(styleKey, value, newSelection)) {
if (!node.allSatisfyInSelection(styleKey, newSelection, (value) {
return value == matchValue;
})) {
return false;
}
}

View File

@ -75,6 +75,11 @@ class Log {
/// For example, uses the logger when processing scroll events.
static Log scroll = Log._(name: 'scroll');
/// For logging message related to [AppFlowyToolbarService].
///
/// For example, uses the logger when processing toolbar events.
static Log toolbar = Log._(name: 'toolbar');
/// For logging message related to UI.
///
/// For example, uses the logger when building the widget.

View File

@ -181,7 +181,12 @@ void _showLinkMenu(EditorState editorState, BuildContext context) {
final length = (selection.start.offset - selection.end.offset).abs();
final node = editorState.service.selectionService.currentSelectedNodes.first
as TextNode;
final linkText = node.getAttributeInSelection(selection, StyleKey.href);
final String linkText;
if (node.allSatisfyLinkInSelection(selection)) {
linkText = node.getAttributeInSelection(selection, StyleKey.href);
} else {
linkText = '';
}
_linkMenuOverlay = OverlayEntry(builder: (context) {
return Positioned(
top: matchRect.bottom + 5.0,

View File

@ -36,10 +36,10 @@ class FlowyService {
// toolbar service
final toolbarServiceKey = GlobalKey(debugLabel: 'flowy_toolbar_service');
FlowyToolbarService? get toolbarService {
AppFlowyToolbarService? get toolbarService {
if (toolbarServiceKey.currentState != null &&
toolbarServiceKey.currentState is FlowyToolbarService) {
return toolbarServiceKey.currentState! as FlowyToolbarService;
toolbarServiceKey.currentState is AppFlowyToolbarService) {
return toolbarServiceKey.currentState! as AppFlowyToolbarService;
}
return null;
}

View File

@ -5,7 +5,7 @@ import 'package:appflowy_editor/appflowy_editor.dart';
import 'package:appflowy_editor/src/render/toolbar/toolbar_widget.dart';
import 'package:appflowy_editor/src/extensions/object_extensions.dart';
abstract class FlowyToolbarService {
abstract class AppFlowyToolbarService {
/// Show the toolbar widget beside the offset.
void showInOffset(Offset offset, LayerLink layerLink);
@ -31,7 +31,7 @@ class FlowyToolbar extends StatefulWidget {
}
class _FlowyToolbarState extends State<FlowyToolbar>
implements FlowyToolbarService {
implements AppFlowyToolbarService {
OverlayEntry? _toolbarOverlay;
final _toolbarWidgetKey = GlobalKey(debugLabel: '_toolbar_widget');

View File

@ -82,7 +82,14 @@ Future<void> _testUpdateTextStyleByCommandX(
);
var textNode = editor.nodeAtPath([1]) as TextNode;
expect(
textNode.allSatisfyInSelection(matchStyle, matchValue, selection), true);
textNode.allSatisfyInSelection(
matchStyle,
selection,
(value) {
return value == matchValue;
},
),
true);
selection =
Selection.single(path: [1], startOffset: 0, endOffset: text.length);
@ -94,7 +101,14 @@ Future<void> _testUpdateTextStyleByCommandX(
);
textNode = editor.nodeAtPath([1]) as TextNode;
expect(
textNode.allSatisfyInSelection(matchStyle, matchValue, selection), true);
textNode.allSatisfyInSelection(
matchStyle,
selection,
(value) {
return value == matchValue;
},
),
true);
await editor.updateSelection(selection);
await editor.pressLogicKey(
@ -123,9 +137,14 @@ Future<void> _testUpdateTextStyleByCommandX(
expect(
node.allSatisfyInSelection(
matchStyle,
matchValue,
Selection.single(
path: node.path, startOffset: 0, endOffset: text.length),
path: node.path,
startOffset: 0,
endOffset: text.length,
),
(value) {
return value == matchValue;
},
),
true,
);