fix: background color issues (#530)

This commit is contained in:
Lucas.Xu 2023-10-11 19:41:15 +08:00 • committed by GitHub
commit 31403c9b42
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
17 changed files with 157 additions and 91 deletions

View file

@ -50,6 +50,7 @@ class BlockSelectionContainer extends StatelessWidget {
? AlignmentDirectional.topStart
: AlignmentDirectional.topEnd,
children: [
// block selection or selection area
BlockSelectionArea(
node: node,
delegate: delegate,
@ -57,9 +58,24 @@ class BlockSelectionContainer extends StatelessWidget {
cursorColor: cursorColor,
selectionColor: selectionColor,
blockColor: blockColor,
supportTypes: supportTypes,
supportTypes: supportTypes
.where(
(element) => element != BlockSelectionType.cursor,
)
.toList(),
),
child,
// cursor
if (supportTypes.contains(BlockSelectionType.cursor))
BlockSelectionArea(
node: node,
delegate: delegate,
listenable: listenable,
cursorColor: cursorColor,
selectionColor: selectionColor,
blockColor: blockColor,
supportTypes: const [BlockSelectionType.cursor],
),
],
);
}

View file

@ -34,6 +34,7 @@ export 'image_block_component/resizable_image.dart';
// numbered list
export 'numbered_list_block_component/numbered_list_block_component.dart';
export 'numbered_list_block_component/numbered_list_character_shortcut.dart';
export 'paragraph_block_component/paragraph_block_component.dart';
// quote
export 'quote_block_component/quote_block_component.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_cell_block_component.dart';
export 'table_block_component/table_commands.dart';
export 'text_block_component/text_block_component.dart';
// to-do list
export 'todo_list_block_component/todo_list_block_component.dart';
export 'todo_list_block_component/todo_list_character_shortcut.dart';

View file

@ -116,7 +116,6 @@ class _BulletedListBlockComponentWidgetState
);
Widget child = Container(
color: withBackgroundColor ? backgroundColor : null,
width: double.infinity,
alignment: alignment,
child: Row(
@ -155,10 +154,13 @@ class _BulletedListBlockComponentWidgetState
),
);
child = Padding(
key: blockComponentKey,
padding: padding,
child: child,
child = Container(
color: withBackgroundColor ? backgroundColor : null,
child: Padding(
key: blockComponentKey,
padding: padding,
child: child,
),
);
child = BlockSelectionContainer(
@ -215,17 +217,14 @@ class _BulletedListIcon extends StatelessWidget {
@override
Widget build(BuildContext context) {
return SizedBox(
width: 22,
height: 22,
child: Padding(
padding: const EdgeInsets.only(right: 5.0),
child: Center(
child: Text(
icon,
style: textStyle,
textScaleFactor: 0.5,
),
return Container(
constraints: const BoxConstraints(minWidth: 26, minHeight: 22),
padding: const EdgeInsets.only(right: 4.0),
child: Center(
child: Text(
icon,
style: textStyle,
textScaleFactor: 0.5,
),
),
);

View file

@ -126,7 +126,6 @@ class _HeadingBlockComponentWidgetState
);
Widget child = Container(
color: backgroundColor,
width: double.infinity,
alignment: alignment,
// Related issue: https://github.com/AppFlowy-IO/AppFlowy/issues/3175
@ -170,10 +169,13 @@ class _HeadingBlockComponentWidgetState
),
);
child = Padding(
key: blockComponentKey,
padding: padding,
child: child,
child = Container(
color: backgroundColor,
child: Padding(
key: blockComponentKey,
padding: padding,
child: child,
),
);
child = BlockSelectionContainer(

View file

@ -38,16 +38,16 @@ void showImageMenu(
}
menuService.dismiss();
imageMenuEntry.remove();
keepEditorFocusNotifier.value -= 1;
keepEditorFocusNotifier.decrease();
}
keepEditorFocusNotifier.value += 1;
keepEditorFocusNotifier.increase();
imageMenuEntry = FullScreenOverlayEntry(
left: left,
right: right,
top: top,
bottom: bottom,
dismissCallback: () => keepEditorFocusNotifier.value -= 1,
dismissCallback: () => keepEditorFocusNotifier.decrease(),
builder: (context) => UploadImageMenu(
backgroundColor: menuService.style.selectionMenuBackgroundColor,
headerColor: menuService.style.selectionMenuItemTextColor,

View file

@ -121,7 +121,6 @@ class _NumberedListBlockComponentWidgetState
);
Widget child = Container(
color: withBackgroundColor ? backgroundColor : null,
width: double.infinity,
alignment: alignment,
child: Row(
@ -161,10 +160,13 @@ class _NumberedListBlockComponentWidgetState
),
);
child = Padding(
key: blockComponentKey,
padding: padding,
child: child,
child = Container(
color: withBackgroundColor ? backgroundColor : null,
child: Padding(
key: blockComponentKey,
padding: padding,
child: child,
),
);
child = BlockSelectionContainer(
@ -206,15 +208,18 @@ class _NumberedListIcon extends StatelessWidget {
final editorState = context.read<EditorState>();
final text = editorState.editorStyle.textStyleConfiguration.text;
final level = _NumberedListIconBuilder(node: node).level;
return Padding(
padding: const EdgeInsets.only(right: 5.0),
child: Text.rich(
textHeightBehavior: const TextHeightBehavior(
applyHeightToFirstAscent: false,
applyHeightToLastDescent: false,
return Container(
constraints: const BoxConstraints(minWidth: 26, minHeight: 22),
padding: const EdgeInsets.only(right: 4.0),
child: Center(
child: Text.rich(
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,
),
);
}

View file

@ -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;
}
}

View file

@ -112,7 +112,6 @@ class _QuoteBlockComponentWidgetState extends State<QuoteBlockComponentWidget>
);
Widget child = Container(
color: backgroundColor,
width: double.infinity,
alignment: alignment,
child: IntrinsicHeight(
@ -150,10 +149,13 @@ class _QuoteBlockComponentWidgetState extends State<QuoteBlockComponentWidget>
),
);
child = Padding(
key: blockComponentKey,
padding: padding,
child: child,
child = Container(
color: backgroundColor,
child: Padding(
key: blockComponentKey,
padding: padding,
child: child,
),
);
child = BlockSelectionContainer(
@ -184,11 +186,17 @@ class _QuoteIcon extends StatelessWidget {
@override
Widget build(BuildContext context) {
return const EditorSvg(
width: 20,
height: 20,
padding: EdgeInsets.only(right: 5.0),
name: 'quote',
return Container(
constraints: const BoxConstraints(minWidth: 26, minHeight: 22),
padding: const EdgeInsets.only(right: 4.0),
child: const Center(
child: EditorSvg(
width: 20,
height: 20,
padding: EdgeInsets.only(right: 4.0),
name: 'quote',
),
),
);
}
}

View file

@ -5,7 +5,7 @@ const standardBlockComponentConfiguration = BlockComponentConfiguration();
final Map<String, BlockComponentBuilder> standardBlockComponentBuilderMap = {
PageBlockKeys.type: PageBlockComponentBuilder(),
ParagraphBlockKeys.type: TextBlockComponentBuilder(
ParagraphBlockKeys.type: ParagraphBlockComponentBuilder(
configuration: standardBlockComponentConfiguration.copyWith(
placeholderText: (_) => PlatformExtension.isDesktopOrWeb
? AppFlowyEditorLocalizations.current.slashPlaceHolder

View file

@ -132,7 +132,6 @@ class _TodoListBlockComponentWidgetState
);
Widget child = Container(
color: withBackgroundColor ? backgroundColor : null,
width: double.infinity,
alignment: alignment,
child: Row(
@ -173,10 +172,13 @@ class _TodoListBlockComponentWidgetState
),
);
child = Padding(
key: blockComponentKey,
padding: padding,
child: child,
child = Container(
color: withBackgroundColor ? backgroundColor : null,
child: Padding(
key: blockComponentKey,
padding: padding,
child: child,
),
);
child = BlockSelectionContainer(
@ -236,11 +238,14 @@ class _TodoListIcon extends StatelessWidget {
child: GestureDetector(
behavior: HitTestBehavior.opaque,
onTap: onTap,
child: EditorSvg(
width: 22,
height: 22,
padding: const EdgeInsets.only(right: 5.0),
name: checked ? 'check' : 'uncheck',
child: Container(
constraints: const BoxConstraints(minWidth: 26, minHeight: 22),
padding: const EdgeInsets.only(right: 4.0),
child: EditorSvg(
width: 22,
height: 22,
name: checked ? 'check' : 'uncheck',
),
),
),
);

View file

@ -1,3 +1,5 @@
import 'dart:math';
import 'package:appflowy_editor/appflowy_editor.dart';
import 'package:appflowy_editor/src/flutter/overlay.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
// only grab the focus when the value is 0
// the operation must be paired
ValueNotifier<int> keepEditorFocusNotifier = ValueNotifier(0);
KeepEditorFocusNotifier keepEditorFocusNotifier = KeepEditorFocusNotifier();
class AppFlowyEditor extends StatefulWidget {
AppFlowyEditor({
@ -267,3 +269,26 @@ class _AppFlowyEditorState extends State<AppFlowyEditor> {
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;
}
}

View file

@ -119,7 +119,7 @@ class NonDeltaTextInputService extends TextInputService with TextInputClient {
@override
void close() {
keepEditorFocusNotifier.value = 0;
keepEditorFocusNotifier.reset();
currentTextEditingValue = null;
composingTextRange = null;
_textInputConnection?.close();

View file

@ -265,7 +265,7 @@ class KeyboardServiceWidgetState extends State<KeyboardServiceWidget>
// clear the selection when the focus is lost.
if (!focusNode.hasFocus) {
if (PlatformExtension.isDesktopOrWeb) {
if (keepEditorFocusNotifier.value > 0) {
if (keepEditorFocusNotifier.shouldKeepFocus) {
return;
}
}
@ -283,7 +283,7 @@ class KeyboardServiceWidgetState extends State<KeyboardServiceWidget>
'keyboard service - on keep editor focus changed: ${keepEditorFocusNotifier.value}}',
);
if (keepEditorFocusNotifier.value == 0) {
if (!keepEditorFocusNotifier.shouldKeepFocus) {
focusNode.requestFocus();
}
}
@ -302,7 +302,10 @@ class KeyboardServiceWidgetState extends State<KeyboardServiceWidget>
if (renderBox != null && selectable != null) {
final size = renderBox.size;
final transform = renderBox.getTransformTo(null);
final rect = selectable.getCursorRectInPosition(selection.end);
final rect = selectable.getCursorRectInPosition(
selection.end,
shiftWithBaseOffset: true,
);
if (rect != null) {
textInputService.updateCaretPosition(size, transform, rect);
}

View file

@ -81,10 +81,10 @@ Future<bool> _showSlashMenu(
if (shouldInsertSlash) {
if (kIsWeb) {
// Have no idea why the focus will lose after inserting on web.
keepEditorFocusNotifier.value += 1;
keepEditorFocusNotifier.increase();
await editorState.insertTextAtPosition('/', position: selection.start);
WidgetsBinding.instance.addPostFrameCallback(
(timeStamp) => keepEditorFocusNotifier.value -= 1,
(timeStamp) => keepEditorFocusNotifier.decrease(),
);
} else {
await editorState.insertTextAtPosition('/', position: selection.start);

View file

@ -268,7 +268,7 @@ class _SelectionMenuWidgetState extends State<SelectionMenuWidget> {
_showingItems = widget.items;
keepEditorFocusNotifier.value += 1;
keepEditorFocusNotifier.increase();
WidgetsBinding.instance.addPostFrameCallback((_) {
_focusNode.requestFocus();
});
@ -277,7 +277,7 @@ class _SelectionMenuWidgetState extends State<SelectionMenuWidget> {
@override
void dispose() {
_focusNode.dispose();
keepEditorFocusNotifier.value -= 1;
keepEditorFocusNotifier.decrease();
super.dispose();
}

View file

@ -37,7 +37,7 @@ void showColorMenu(
overlay = null;
}
keepEditorFocusNotifier.value += 1;
keepEditorFocusNotifier.increase();
overlay = FullScreenOverlayEntry(
top: top,
bottom: bottom,
@ -67,7 +67,7 @@ void showColorMenu(
withUpdateSelection: true,
);
dismissOverlay();
keepEditorFocusNotifier.value -= 1;
keepEditorFocusNotifier.decrease();
},
resetText: isTextColor
? AppFlowyEditorLocalizations.current.resetToDefaultColor

View file

@ -63,18 +63,18 @@ void showLinkMenu(
OverlayEntry? overlay;
void dismissOverlay() {
keepEditorFocusNotifier.value -= 1;
keepEditorFocusNotifier.decrease();
overlay?.remove();
overlay = null;
}
keepEditorFocusNotifier.value += 1;
keepEditorFocusNotifier.increase();
overlay = FullScreenOverlayEntry(
top: top,
bottom: bottom,
left: left,
right: right,
dismissCallback: () => keepEditorFocusNotifier.value -= 1,
dismissCallback: () => keepEditorFocusNotifier.decrease(),
builder: (context) {
return LinkMenu(
linkText: linkText,