fix: background color issues (#530)
This commit is contained in:
parent
b5f8bd11f1
commit
31403c9b42
20 changed files with 157 additions and 91 deletions
|
|
@ -142,7 +142,7 @@ class _MobileEditorState extends State<MobileEditor> {
|
||||||
fontWeight: FontWeight.w600,
|
fontWeight: FontWeight.w600,
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
map[ParagraphBlockKeys.type] = TextBlockComponentBuilder(
|
map[ParagraphBlockKeys.type] = ParagraphBlockComponentBuilder(
|
||||||
configuration: BlockComponentConfiguration(
|
configuration: BlockComponentConfiguration(
|
||||||
placeholderText: (node) => 'Type something...',
|
placeholderText: (node) => 'Type something...',
|
||||||
),
|
),
|
||||||
|
|
|
||||||
|
|
@ -50,6 +50,7 @@ class BlockSelectionContainer extends StatelessWidget {
|
||||||
? AlignmentDirectional.topStart
|
? AlignmentDirectional.topStart
|
||||||
: AlignmentDirectional.topEnd,
|
: AlignmentDirectional.topEnd,
|
||||||
children: [
|
children: [
|
||||||
|
// block selection or selection area
|
||||||
BlockSelectionArea(
|
BlockSelectionArea(
|
||||||
node: node,
|
node: node,
|
||||||
delegate: delegate,
|
delegate: delegate,
|
||||||
|
|
@ -57,9 +58,24 @@ class BlockSelectionContainer extends StatelessWidget {
|
||||||
cursorColor: cursorColor,
|
cursorColor: cursorColor,
|
||||||
selectionColor: selectionColor,
|
selectionColor: selectionColor,
|
||||||
blockColor: blockColor,
|
blockColor: blockColor,
|
||||||
supportTypes: supportTypes,
|
supportTypes: supportTypes
|
||||||
|
.where(
|
||||||
|
(element) => element != BlockSelectionType.cursor,
|
||||||
|
)
|
||||||
|
.toList(),
|
||||||
),
|
),
|
||||||
child,
|
child,
|
||||||
|
// cursor
|
||||||
|
if (supportTypes.contains(BlockSelectionType.cursor))
|
||||||
|
BlockSelectionArea(
|
||||||
|
node: node,
|
||||||
|
delegate: delegate,
|
||||||
|
listenable: listenable,
|
||||||
|
cursorColor: cursorColor,
|
||||||
|
selectionColor: selectionColor,
|
||||||
|
blockColor: blockColor,
|
||||||
|
supportTypes: const [BlockSelectionType.cursor],
|
||||||
|
),
|
||||||
],
|
],
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -34,6 +34,7 @@ export 'image_block_component/resizable_image.dart';
|
||||||
// numbered list
|
// numbered list
|
||||||
export 'numbered_list_block_component/numbered_list_block_component.dart';
|
export 'numbered_list_block_component/numbered_list_block_component.dart';
|
||||||
export 'numbered_list_block_component/numbered_list_character_shortcut.dart';
|
export 'numbered_list_block_component/numbered_list_character_shortcut.dart';
|
||||||
|
export 'paragraph_block_component/paragraph_block_component.dart';
|
||||||
// quote
|
// quote
|
||||||
export 'quote_block_component/quote_block_component.dart';
|
export 'quote_block_component/quote_block_component.dart';
|
||||||
export 'quote_block_component/quote_character_shortcut.dart';
|
export 'quote_block_component/quote_character_shortcut.dart';
|
||||||
|
|
@ -47,7 +48,6 @@ export 'table_block_component/table_action.dart';
|
||||||
export 'table_block_component/table_block_component.dart';
|
export 'table_block_component/table_block_component.dart';
|
||||||
export 'table_block_component/table_cell_block_component.dart';
|
export 'table_block_component/table_cell_block_component.dart';
|
||||||
export 'table_block_component/table_commands.dart';
|
export 'table_block_component/table_commands.dart';
|
||||||
export 'text_block_component/text_block_component.dart';
|
|
||||||
// to-do list
|
// to-do list
|
||||||
export 'todo_list_block_component/todo_list_block_component.dart';
|
export 'todo_list_block_component/todo_list_block_component.dart';
|
||||||
export 'todo_list_block_component/todo_list_character_shortcut.dart';
|
export 'todo_list_block_component/todo_list_character_shortcut.dart';
|
||||||
|
|
|
||||||
|
|
@ -116,7 +116,6 @@ class _BulletedListBlockComponentWidgetState
|
||||||
);
|
);
|
||||||
|
|
||||||
Widget child = Container(
|
Widget child = Container(
|
||||||
color: withBackgroundColor ? backgroundColor : null,
|
|
||||||
width: double.infinity,
|
width: double.infinity,
|
||||||
alignment: alignment,
|
alignment: alignment,
|
||||||
child: Row(
|
child: Row(
|
||||||
|
|
@ -155,10 +154,13 @@ class _BulletedListBlockComponentWidgetState
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
|
|
||||||
child = Padding(
|
child = Container(
|
||||||
key: blockComponentKey,
|
color: withBackgroundColor ? backgroundColor : null,
|
||||||
padding: padding,
|
child: Padding(
|
||||||
child: child,
|
key: blockComponentKey,
|
||||||
|
padding: padding,
|
||||||
|
child: child,
|
||||||
|
),
|
||||||
);
|
);
|
||||||
|
|
||||||
child = BlockSelectionContainer(
|
child = BlockSelectionContainer(
|
||||||
|
|
@ -215,17 +217,14 @@ class _BulletedListIcon extends StatelessWidget {
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
return SizedBox(
|
return Container(
|
||||||
width: 22,
|
constraints: const BoxConstraints(minWidth: 26, minHeight: 22),
|
||||||
height: 22,
|
padding: const EdgeInsets.only(right: 4.0),
|
||||||
child: Padding(
|
child: Center(
|
||||||
padding: const EdgeInsets.only(right: 5.0),
|
child: Text(
|
||||||
child: Center(
|
icon,
|
||||||
child: Text(
|
style: textStyle,
|
||||||
icon,
|
textScaleFactor: 0.5,
|
||||||
style: textStyle,
|
|
||||||
textScaleFactor: 0.5,
|
|
||||||
),
|
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
|
|
|
||||||
|
|
@ -126,7 +126,6 @@ class _HeadingBlockComponentWidgetState
|
||||||
);
|
);
|
||||||
|
|
||||||
Widget child = Container(
|
Widget child = Container(
|
||||||
color: backgroundColor,
|
|
||||||
width: double.infinity,
|
width: double.infinity,
|
||||||
alignment: alignment,
|
alignment: alignment,
|
||||||
// Related issue: https://github.com/AppFlowy-IO/AppFlowy/issues/3175
|
// Related issue: https://github.com/AppFlowy-IO/AppFlowy/issues/3175
|
||||||
|
|
@ -170,10 +169,13 @@ class _HeadingBlockComponentWidgetState
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
|
|
||||||
child = Padding(
|
child = Container(
|
||||||
key: blockComponentKey,
|
color: backgroundColor,
|
||||||
padding: padding,
|
child: Padding(
|
||||||
child: child,
|
key: blockComponentKey,
|
||||||
|
padding: padding,
|
||||||
|
child: child,
|
||||||
|
),
|
||||||
);
|
);
|
||||||
|
|
||||||
child = BlockSelectionContainer(
|
child = BlockSelectionContainer(
|
||||||
|
|
|
||||||
|
|
@ -38,16 +38,16 @@ void showImageMenu(
|
||||||
}
|
}
|
||||||
menuService.dismiss();
|
menuService.dismiss();
|
||||||
imageMenuEntry.remove();
|
imageMenuEntry.remove();
|
||||||
keepEditorFocusNotifier.value -= 1;
|
keepEditorFocusNotifier.decrease();
|
||||||
}
|
}
|
||||||
|
|
||||||
keepEditorFocusNotifier.value += 1;
|
keepEditorFocusNotifier.increase();
|
||||||
imageMenuEntry = FullScreenOverlayEntry(
|
imageMenuEntry = FullScreenOverlayEntry(
|
||||||
left: left,
|
left: left,
|
||||||
right: right,
|
right: right,
|
||||||
top: top,
|
top: top,
|
||||||
bottom: bottom,
|
bottom: bottom,
|
||||||
dismissCallback: () => keepEditorFocusNotifier.value -= 1,
|
dismissCallback: () => keepEditorFocusNotifier.decrease(),
|
||||||
builder: (context) => UploadImageMenu(
|
builder: (context) => UploadImageMenu(
|
||||||
backgroundColor: menuService.style.selectionMenuBackgroundColor,
|
backgroundColor: menuService.style.selectionMenuBackgroundColor,
|
||||||
headerColor: menuService.style.selectionMenuItemTextColor,
|
headerColor: menuService.style.selectionMenuItemTextColor,
|
||||||
|
|
|
||||||
|
|
@ -121,7 +121,6 @@ class _NumberedListBlockComponentWidgetState
|
||||||
);
|
);
|
||||||
|
|
||||||
Widget child = Container(
|
Widget child = Container(
|
||||||
color: withBackgroundColor ? backgroundColor : null,
|
|
||||||
width: double.infinity,
|
width: double.infinity,
|
||||||
alignment: alignment,
|
alignment: alignment,
|
||||||
child: Row(
|
child: Row(
|
||||||
|
|
@ -161,10 +160,13 @@ class _NumberedListBlockComponentWidgetState
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
|
|
||||||
child = Padding(
|
child = Container(
|
||||||
key: blockComponentKey,
|
color: withBackgroundColor ? backgroundColor : null,
|
||||||
padding: padding,
|
child: Padding(
|
||||||
child: child,
|
key: blockComponentKey,
|
||||||
|
padding: padding,
|
||||||
|
child: child,
|
||||||
|
),
|
||||||
);
|
);
|
||||||
|
|
||||||
child = BlockSelectionContainer(
|
child = BlockSelectionContainer(
|
||||||
|
|
@ -206,15 +208,18 @@ class _NumberedListIcon extends StatelessWidget {
|
||||||
final editorState = context.read<EditorState>();
|
final editorState = context.read<EditorState>();
|
||||||
final text = editorState.editorStyle.textStyleConfiguration.text;
|
final text = editorState.editorStyle.textStyleConfiguration.text;
|
||||||
final level = _NumberedListIconBuilder(node: node).level;
|
final level = _NumberedListIconBuilder(node: node).level;
|
||||||
return Padding(
|
return Container(
|
||||||
padding: const EdgeInsets.only(right: 5.0),
|
constraints: const BoxConstraints(minWidth: 26, minHeight: 22),
|
||||||
child: Text.rich(
|
padding: const EdgeInsets.only(right: 4.0),
|
||||||
textHeightBehavior: const TextHeightBehavior(
|
child: Center(
|
||||||
applyHeightToFirstAscent: false,
|
child: Text.rich(
|
||||||
applyHeightToLastDescent: false,
|
textHeightBehavior: const TextHeightBehavior(
|
||||||
|
applyHeightToFirstAscent: false,
|
||||||
|
applyHeightToLastDescent: false,
|
||||||
|
),
|
||||||
|
TextSpan(text: '$level.', style: text.combine(textStyle)),
|
||||||
|
textDirection: direction,
|
||||||
),
|
),
|
||||||
TextSpan(text: '$level.', style: text.combine(textStyle)),
|
|
||||||
textDirection: direction,
|
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,194 @@
|
||||||
|
import 'package:appflowy_editor/appflowy_editor.dart';
|
||||||
|
import 'package:flutter/material.dart';
|
||||||
|
|
||||||
|
class ParagraphBlockKeys {
|
||||||
|
ParagraphBlockKeys._();
|
||||||
|
|
||||||
|
static const String type = 'paragraph';
|
||||||
|
|
||||||
|
static const String delta = blockComponentDelta;
|
||||||
|
|
||||||
|
static const String backgroundColor = blockComponentBackgroundColor;
|
||||||
|
|
||||||
|
static const String textDirection = blockComponentTextDirection;
|
||||||
|
}
|
||||||
|
|
||||||
|
Node paragraphNode({
|
||||||
|
String? text,
|
||||||
|
Delta? delta,
|
||||||
|
String? textDirection,
|
||||||
|
Attributes? attributes,
|
||||||
|
Iterable<Node> children = const [],
|
||||||
|
}) {
|
||||||
|
return Node(
|
||||||
|
type: ParagraphBlockKeys.type,
|
||||||
|
attributes: {
|
||||||
|
ParagraphBlockKeys.delta:
|
||||||
|
(delta ?? (Delta()..insert(text ?? ''))).toJson(),
|
||||||
|
if (attributes != null) ...attributes,
|
||||||
|
if (textDirection != null)
|
||||||
|
ParagraphBlockKeys.textDirection: textDirection,
|
||||||
|
},
|
||||||
|
children: children,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
class ParagraphBlockComponentBuilder extends BlockComponentBuilder {
|
||||||
|
ParagraphBlockComponentBuilder({
|
||||||
|
super.configuration,
|
||||||
|
});
|
||||||
|
|
||||||
|
@override
|
||||||
|
BlockComponentWidget build(BlockComponentContext blockComponentContext) {
|
||||||
|
final node = blockComponentContext.node;
|
||||||
|
return ParagraphBlockComponentWidget(
|
||||||
|
node: node,
|
||||||
|
key: node.key,
|
||||||
|
configuration: configuration,
|
||||||
|
showActions: showActions(node),
|
||||||
|
actionBuilder: (context, state) => actionBuilder(
|
||||||
|
blockComponentContext,
|
||||||
|
state,
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
bool validate(Node node) {
|
||||||
|
return node.delta != null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
class ParagraphBlockComponentWidget extends BlockComponentStatefulWidget {
|
||||||
|
const ParagraphBlockComponentWidget({
|
||||||
|
super.key,
|
||||||
|
required super.node,
|
||||||
|
super.showActions,
|
||||||
|
super.actionBuilder,
|
||||||
|
super.configuration = const BlockComponentConfiguration(),
|
||||||
|
});
|
||||||
|
|
||||||
|
@override
|
||||||
|
State<ParagraphBlockComponentWidget> createState() =>
|
||||||
|
_ParagraphBlockComponentWidgetState();
|
||||||
|
}
|
||||||
|
|
||||||
|
class _ParagraphBlockComponentWidgetState
|
||||||
|
extends State<ParagraphBlockComponentWidget>
|
||||||
|
with
|
||||||
|
SelectableMixin,
|
||||||
|
DefaultSelectableMixin,
|
||||||
|
BlockComponentConfigurable,
|
||||||
|
BlockComponentBackgroundColorMixin,
|
||||||
|
NestedBlockComponentStatefulWidgetMixin,
|
||||||
|
BlockComponentTextDirectionMixin,
|
||||||
|
BlockComponentAlignMixin {
|
||||||
|
@override
|
||||||
|
final forwardKey = GlobalKey(debugLabel: 'flowy_rich_text');
|
||||||
|
|
||||||
|
@override
|
||||||
|
GlobalKey<State<StatefulWidget>> get containerKey => widget.node.key;
|
||||||
|
|
||||||
|
@override
|
||||||
|
GlobalKey<State<StatefulWidget>> blockComponentKey = GlobalKey(
|
||||||
|
debugLabel: ParagraphBlockKeys.type,
|
||||||
|
);
|
||||||
|
|
||||||
|
@override
|
||||||
|
BlockComponentConfiguration get configuration => widget.configuration;
|
||||||
|
|
||||||
|
@override
|
||||||
|
Node get node => widget.node;
|
||||||
|
|
||||||
|
bool _showPlaceholder = false;
|
||||||
|
|
||||||
|
@override
|
||||||
|
void initState() {
|
||||||
|
super.initState();
|
||||||
|
editorState.selectionNotifier.addListener(_onSelectionChange);
|
||||||
|
_onSelectionChange();
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
void dispose() {
|
||||||
|
editorState.selectionNotifier.removeListener(_onSelectionChange);
|
||||||
|
super.dispose();
|
||||||
|
}
|
||||||
|
|
||||||
|
void _onSelectionChange() {
|
||||||
|
final selection = editorState.selection;
|
||||||
|
final showPlaceholder = selection != null &&
|
||||||
|
(selection.isSingle && selection.start.path.equals(node.path));
|
||||||
|
if (showPlaceholder != _showPlaceholder) {
|
||||||
|
setState(() => _showPlaceholder = showPlaceholder);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
Widget buildComponent(
|
||||||
|
BuildContext context, {
|
||||||
|
bool withBackgroundColor = true,
|
||||||
|
}) {
|
||||||
|
final textDirection = calculateTextDirection(
|
||||||
|
layoutDirection: Directionality.maybeOf(context),
|
||||||
|
);
|
||||||
|
|
||||||
|
Widget child = Container(
|
||||||
|
width: double.infinity,
|
||||||
|
alignment: alignment,
|
||||||
|
child: Column(
|
||||||
|
crossAxisAlignment: CrossAxisAlignment.start,
|
||||||
|
mainAxisAlignment: MainAxisAlignment.start,
|
||||||
|
mainAxisSize: MainAxisSize.min,
|
||||||
|
textDirection: textDirection,
|
||||||
|
children: [
|
||||||
|
AppFlowyRichText(
|
||||||
|
key: forwardKey,
|
||||||
|
delegate: this,
|
||||||
|
node: widget.node,
|
||||||
|
editorState: editorState,
|
||||||
|
textAlign: alignment?.toTextAlign,
|
||||||
|
placeholderText: _showPlaceholder ? placeholderText : ' ',
|
||||||
|
textSpanDecorator: (textSpan) =>
|
||||||
|
textSpan.updateTextStyle(textStyle),
|
||||||
|
placeholderTextSpanDecorator: (textSpan) =>
|
||||||
|
textSpan.updateTextStyle(placeholderTextStyle),
|
||||||
|
textDirection: textDirection,
|
||||||
|
cursorColor: editorState.editorStyle.cursorColor,
|
||||||
|
selectionColor: editorState.editorStyle.selectionColor,
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
);
|
||||||
|
|
||||||
|
child = Container(
|
||||||
|
color: withBackgroundColor ? backgroundColor : null,
|
||||||
|
child: Padding(
|
||||||
|
key: blockComponentKey,
|
||||||
|
padding: padding,
|
||||||
|
child: child,
|
||||||
|
),
|
||||||
|
);
|
||||||
|
|
||||||
|
child = BlockSelectionContainer(
|
||||||
|
node: node,
|
||||||
|
delegate: this,
|
||||||
|
listenable: editorState.selectionNotifier,
|
||||||
|
blockColor: editorState.editorStyle.selectionColor,
|
||||||
|
supportTypes: const [
|
||||||
|
BlockSelectionType.block,
|
||||||
|
],
|
||||||
|
child: child,
|
||||||
|
);
|
||||||
|
|
||||||
|
if (widget.showActions && widget.actionBuilder != null) {
|
||||||
|
child = BlockComponentActionWrapper(
|
||||||
|
node: node,
|
||||||
|
actionBuilder: widget.actionBuilder!,
|
||||||
|
child: child,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
return child;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -112,7 +112,6 @@ class _QuoteBlockComponentWidgetState extends State<QuoteBlockComponentWidget>
|
||||||
);
|
);
|
||||||
|
|
||||||
Widget child = Container(
|
Widget child = Container(
|
||||||
color: backgroundColor,
|
|
||||||
width: double.infinity,
|
width: double.infinity,
|
||||||
alignment: alignment,
|
alignment: alignment,
|
||||||
child: IntrinsicHeight(
|
child: IntrinsicHeight(
|
||||||
|
|
@ -150,10 +149,13 @@ class _QuoteBlockComponentWidgetState extends State<QuoteBlockComponentWidget>
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
|
|
||||||
child = Padding(
|
child = Container(
|
||||||
key: blockComponentKey,
|
color: backgroundColor,
|
||||||
padding: padding,
|
child: Padding(
|
||||||
child: child,
|
key: blockComponentKey,
|
||||||
|
padding: padding,
|
||||||
|
child: child,
|
||||||
|
),
|
||||||
);
|
);
|
||||||
|
|
||||||
child = BlockSelectionContainer(
|
child = BlockSelectionContainer(
|
||||||
|
|
@ -184,11 +186,17 @@ class _QuoteIcon extends StatelessWidget {
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
return const EditorSvg(
|
return Container(
|
||||||
width: 20,
|
constraints: const BoxConstraints(minWidth: 26, minHeight: 22),
|
||||||
height: 20,
|
padding: const EdgeInsets.only(right: 4.0),
|
||||||
padding: EdgeInsets.only(right: 5.0),
|
child: const Center(
|
||||||
name: 'quote',
|
child: EditorSvg(
|
||||||
|
width: 20,
|
||||||
|
height: 20,
|
||||||
|
padding: EdgeInsets.only(right: 4.0),
|
||||||
|
name: 'quote',
|
||||||
|
),
|
||||||
|
),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -5,7 +5,7 @@ const standardBlockComponentConfiguration = BlockComponentConfiguration();
|
||||||
|
|
||||||
final Map<String, BlockComponentBuilder> standardBlockComponentBuilderMap = {
|
final Map<String, BlockComponentBuilder> standardBlockComponentBuilderMap = {
|
||||||
PageBlockKeys.type: PageBlockComponentBuilder(),
|
PageBlockKeys.type: PageBlockComponentBuilder(),
|
||||||
ParagraphBlockKeys.type: TextBlockComponentBuilder(
|
ParagraphBlockKeys.type: ParagraphBlockComponentBuilder(
|
||||||
configuration: standardBlockComponentConfiguration.copyWith(
|
configuration: standardBlockComponentConfiguration.copyWith(
|
||||||
placeholderText: (_) => PlatformExtension.isDesktopOrWeb
|
placeholderText: (_) => PlatformExtension.isDesktopOrWeb
|
||||||
? AppFlowyEditorLocalizations.current.slashPlaceHolder
|
? AppFlowyEditorLocalizations.current.slashPlaceHolder
|
||||||
|
|
|
||||||
|
|
@ -132,7 +132,6 @@ class _TodoListBlockComponentWidgetState
|
||||||
);
|
);
|
||||||
|
|
||||||
Widget child = Container(
|
Widget child = Container(
|
||||||
color: withBackgroundColor ? backgroundColor : null,
|
|
||||||
width: double.infinity,
|
width: double.infinity,
|
||||||
alignment: alignment,
|
alignment: alignment,
|
||||||
child: Row(
|
child: Row(
|
||||||
|
|
@ -173,10 +172,13 @@ class _TodoListBlockComponentWidgetState
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
|
|
||||||
child = Padding(
|
child = Container(
|
||||||
key: blockComponentKey,
|
color: withBackgroundColor ? backgroundColor : null,
|
||||||
padding: padding,
|
child: Padding(
|
||||||
child: child,
|
key: blockComponentKey,
|
||||||
|
padding: padding,
|
||||||
|
child: child,
|
||||||
|
),
|
||||||
);
|
);
|
||||||
|
|
||||||
child = BlockSelectionContainer(
|
child = BlockSelectionContainer(
|
||||||
|
|
@ -236,11 +238,14 @@ class _TodoListIcon extends StatelessWidget {
|
||||||
child: GestureDetector(
|
child: GestureDetector(
|
||||||
behavior: HitTestBehavior.opaque,
|
behavior: HitTestBehavior.opaque,
|
||||||
onTap: onTap,
|
onTap: onTap,
|
||||||
child: EditorSvg(
|
child: Container(
|
||||||
width: 22,
|
constraints: const BoxConstraints(minWidth: 26, minHeight: 22),
|
||||||
height: 22,
|
padding: const EdgeInsets.only(right: 4.0),
|
||||||
padding: const EdgeInsets.only(right: 5.0),
|
child: EditorSvg(
|
||||||
name: checked ? 'check' : 'uncheck',
|
width: 22,
|
||||||
|
height: 22,
|
||||||
|
name: checked ? 'check' : 'uncheck',
|
||||||
|
),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
|
|
|
||||||
|
|
@ -1,3 +1,5 @@
|
||||||
|
import 'dart:math';
|
||||||
|
|
||||||
import 'package:appflowy_editor/appflowy_editor.dart';
|
import 'package:appflowy_editor/appflowy_editor.dart';
|
||||||
import 'package:appflowy_editor/src/flutter/overlay.dart';
|
import 'package:appflowy_editor/src/flutter/overlay.dart';
|
||||||
import 'package:appflowy_editor/src/service/context_menu/built_in_context_menu_item.dart';
|
import 'package:appflowy_editor/src/service/context_menu/built_in_context_menu_item.dart';
|
||||||
|
|
@ -11,7 +13,7 @@ import 'package:provider/provider.dart';
|
||||||
// decrease the value when the popover is closed
|
// decrease the value when the popover is closed
|
||||||
// only grab the focus when the value is 0
|
// only grab the focus when the value is 0
|
||||||
// the operation must be paired
|
// the operation must be paired
|
||||||
ValueNotifier<int> keepEditorFocusNotifier = ValueNotifier(0);
|
KeepEditorFocusNotifier keepEditorFocusNotifier = KeepEditorFocusNotifier();
|
||||||
|
|
||||||
class AppFlowyEditor extends StatefulWidget {
|
class AppFlowyEditor extends StatefulWidget {
|
||||||
AppFlowyEditor({
|
AppFlowyEditor({
|
||||||
|
|
@ -267,3 +269,26 @@ class _AppFlowyEditorState extends State<AppFlowyEditor> {
|
||||||
builders: {...widget.blockComponentBuilders},
|
builders: {...widget.blockComponentBuilders},
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
class KeepEditorFocusNotifier extends ValueNotifier<int> {
|
||||||
|
KeepEditorFocusNotifier() : super(0);
|
||||||
|
|
||||||
|
bool get shouldKeepFocus => value > 0;
|
||||||
|
|
||||||
|
@override
|
||||||
|
set value(int v) {
|
||||||
|
super.value = max(0, v);
|
||||||
|
}
|
||||||
|
|
||||||
|
void increase() {
|
||||||
|
value++;
|
||||||
|
}
|
||||||
|
|
||||||
|
void decrease() {
|
||||||
|
value--;
|
||||||
|
}
|
||||||
|
|
||||||
|
void reset() {
|
||||||
|
value = 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
||||||
|
|
@ -119,7 +119,7 @@ class NonDeltaTextInputService extends TextInputService with TextInputClient {
|
||||||
|
|
||||||
@override
|
@override
|
||||||
void close() {
|
void close() {
|
||||||
keepEditorFocusNotifier.value = 0;
|
keepEditorFocusNotifier.reset();
|
||||||
currentTextEditingValue = null;
|
currentTextEditingValue = null;
|
||||||
composingTextRange = null;
|
composingTextRange = null;
|
||||||
_textInputConnection?.close();
|
_textInputConnection?.close();
|
||||||
|
|
|
||||||
|
|
@ -265,7 +265,7 @@ class KeyboardServiceWidgetState extends State<KeyboardServiceWidget>
|
||||||
// clear the selection when the focus is lost.
|
// clear the selection when the focus is lost.
|
||||||
if (!focusNode.hasFocus) {
|
if (!focusNode.hasFocus) {
|
||||||
if (PlatformExtension.isDesktopOrWeb) {
|
if (PlatformExtension.isDesktopOrWeb) {
|
||||||
if (keepEditorFocusNotifier.value > 0) {
|
if (keepEditorFocusNotifier.shouldKeepFocus) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -283,7 +283,7 @@ class KeyboardServiceWidgetState extends State<KeyboardServiceWidget>
|
||||||
'keyboard service - on keep editor focus changed: ${keepEditorFocusNotifier.value}}',
|
'keyboard service - on keep editor focus changed: ${keepEditorFocusNotifier.value}}',
|
||||||
);
|
);
|
||||||
|
|
||||||
if (keepEditorFocusNotifier.value == 0) {
|
if (!keepEditorFocusNotifier.shouldKeepFocus) {
|
||||||
focusNode.requestFocus();
|
focusNode.requestFocus();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -302,7 +302,10 @@ class KeyboardServiceWidgetState extends State<KeyboardServiceWidget>
|
||||||
if (renderBox != null && selectable != null) {
|
if (renderBox != null && selectable != null) {
|
||||||
final size = renderBox.size;
|
final size = renderBox.size;
|
||||||
final transform = renderBox.getTransformTo(null);
|
final transform = renderBox.getTransformTo(null);
|
||||||
final rect = selectable.getCursorRectInPosition(selection.end);
|
final rect = selectable.getCursorRectInPosition(
|
||||||
|
selection.end,
|
||||||
|
shiftWithBaseOffset: true,
|
||||||
|
);
|
||||||
if (rect != null) {
|
if (rect != null) {
|
||||||
textInputService.updateCaretPosition(size, transform, rect);
|
textInputService.updateCaretPosition(size, transform, rect);
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -81,10 +81,10 @@ Future<bool> _showSlashMenu(
|
||||||
if (shouldInsertSlash) {
|
if (shouldInsertSlash) {
|
||||||
if (kIsWeb) {
|
if (kIsWeb) {
|
||||||
// Have no idea why the focus will lose after inserting on web.
|
// Have no idea why the focus will lose after inserting on web.
|
||||||
keepEditorFocusNotifier.value += 1;
|
keepEditorFocusNotifier.increase();
|
||||||
await editorState.insertTextAtPosition('/', position: selection.start);
|
await editorState.insertTextAtPosition('/', position: selection.start);
|
||||||
WidgetsBinding.instance.addPostFrameCallback(
|
WidgetsBinding.instance.addPostFrameCallback(
|
||||||
(timeStamp) => keepEditorFocusNotifier.value -= 1,
|
(timeStamp) => keepEditorFocusNotifier.decrease(),
|
||||||
);
|
);
|
||||||
} else {
|
} else {
|
||||||
await editorState.insertTextAtPosition('/', position: selection.start);
|
await editorState.insertTextAtPosition('/', position: selection.start);
|
||||||
|
|
|
||||||
|
|
@ -268,7 +268,7 @@ class _SelectionMenuWidgetState extends State<SelectionMenuWidget> {
|
||||||
|
|
||||||
_showingItems = widget.items;
|
_showingItems = widget.items;
|
||||||
|
|
||||||
keepEditorFocusNotifier.value += 1;
|
keepEditorFocusNotifier.increase();
|
||||||
WidgetsBinding.instance.addPostFrameCallback((_) {
|
WidgetsBinding.instance.addPostFrameCallback((_) {
|
||||||
_focusNode.requestFocus();
|
_focusNode.requestFocus();
|
||||||
});
|
});
|
||||||
|
|
@ -277,7 +277,7 @@ class _SelectionMenuWidgetState extends State<SelectionMenuWidget> {
|
||||||
@override
|
@override
|
||||||
void dispose() {
|
void dispose() {
|
||||||
_focusNode.dispose();
|
_focusNode.dispose();
|
||||||
keepEditorFocusNotifier.value -= 1;
|
keepEditorFocusNotifier.decrease();
|
||||||
|
|
||||||
super.dispose();
|
super.dispose();
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -37,7 +37,7 @@ void showColorMenu(
|
||||||
overlay = null;
|
overlay = null;
|
||||||
}
|
}
|
||||||
|
|
||||||
keepEditorFocusNotifier.value += 1;
|
keepEditorFocusNotifier.increase();
|
||||||
overlay = FullScreenOverlayEntry(
|
overlay = FullScreenOverlayEntry(
|
||||||
top: top,
|
top: top,
|
||||||
bottom: bottom,
|
bottom: bottom,
|
||||||
|
|
@ -67,7 +67,7 @@ void showColorMenu(
|
||||||
withUpdateSelection: true,
|
withUpdateSelection: true,
|
||||||
);
|
);
|
||||||
dismissOverlay();
|
dismissOverlay();
|
||||||
keepEditorFocusNotifier.value -= 1;
|
keepEditorFocusNotifier.decrease();
|
||||||
},
|
},
|
||||||
resetText: isTextColor
|
resetText: isTextColor
|
||||||
? AppFlowyEditorLocalizations.current.resetToDefaultColor
|
? AppFlowyEditorLocalizations.current.resetToDefaultColor
|
||||||
|
|
|
||||||
|
|
@ -63,18 +63,18 @@ void showLinkMenu(
|
||||||
OverlayEntry? overlay;
|
OverlayEntry? overlay;
|
||||||
|
|
||||||
void dismissOverlay() {
|
void dismissOverlay() {
|
||||||
keepEditorFocusNotifier.value -= 1;
|
keepEditorFocusNotifier.decrease();
|
||||||
overlay?.remove();
|
overlay?.remove();
|
||||||
overlay = null;
|
overlay = null;
|
||||||
}
|
}
|
||||||
|
|
||||||
keepEditorFocusNotifier.value += 1;
|
keepEditorFocusNotifier.increase();
|
||||||
overlay = FullScreenOverlayEntry(
|
overlay = FullScreenOverlayEntry(
|
||||||
top: top,
|
top: top,
|
||||||
bottom: bottom,
|
bottom: bottom,
|
||||||
left: left,
|
left: left,
|
||||||
right: right,
|
right: right,
|
||||||
dismissCallback: () => keepEditorFocusNotifier.value -= 1,
|
dismissCallback: () => keepEditorFocusNotifier.decrease(),
|
||||||
builder: (context) {
|
builder: (context) {
|
||||||
return LinkMenu(
|
return LinkMenu(
|
||||||
linkText: linkText,
|
linkText: linkText,
|
||||||
|
|
|
||||||
|
|
@ -21,7 +21,7 @@ void main() async {
|
||||||
addTearDown(gesture.removePointer);
|
addTearDown(gesture.removePointer);
|
||||||
await tester.pump();
|
await tester.pump();
|
||||||
await gesture.moveTo(
|
await gesture.moveTo(
|
||||||
tester.getCenter(find.byType(TextBlockComponentWidget)),
|
tester.getCenter(find.byType(ParagraphBlockComponentWidget)),
|
||||||
);
|
);
|
||||||
await tester.pumpAndSettle(const Duration(milliseconds: 500));
|
await tester.pumpAndSettle(const Duration(milliseconds: 500));
|
||||||
|
|
||||||
|
|
@ -60,7 +60,7 @@ class CustomActionBuilder extends StatelessWidget {
|
||||||
|
|
||||||
final editorState = EditorState(document: document);
|
final editorState = EditorState(document: document);
|
||||||
|
|
||||||
final paragraphBuilder = TextBlockComponentBuilder(
|
final paragraphBuilder = ParagraphBlockComponentBuilder(
|
||||||
configuration: standardBlockComponentConfiguration,
|
configuration: standardBlockComponentConfiguration,
|
||||||
);
|
);
|
||||||
paragraphBuilder.showActions = (_) => true;
|
paragraphBuilder.showActions = (_) => true;
|
||||||
|
|
|
||||||
|
|
@ -50,7 +50,7 @@ void main() async {
|
||||||
expect(editorState.selection, null);
|
expect(editorState.selection, null);
|
||||||
|
|
||||||
await tester.tapAt(
|
await tester.tapAt(
|
||||||
tester.getTopLeft(find.byType(TextBlockComponentWidget)),
|
tester.getTopLeft(find.byType(ParagraphBlockComponentWidget)),
|
||||||
);
|
);
|
||||||
await tester.pumpAndSettle();
|
await tester.pumpAndSettle();
|
||||||
expect(widget.focusNode.hasFocus, false);
|
expect(widget.focusNode.hasFocus, false);
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue