feat: support customizing font and color before typing (#4765)

* fix: bius buttons highlight status

* feat: support customizing font family before typing

* feat: support customizing text color before typing
This commit is contained in:
Lucas.Xu 2024-02-28 14:51:50 +07:00 committed by GitHub
parent e250f780a4
commit abd08e5e53
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
5 changed files with 149 additions and 47 deletions

View File

@ -72,7 +72,7 @@ class BIUSItems extends StatelessWidget {
}, },
icon: icon, icon: icon,
isSelected: editorState.isTextDecorationSelected(richTextKey) && isSelected: editorState.isTextDecorationSelected(richTextKey) &&
editorState.toggledStyle[richTextKey] != true, editorState.toggledStyle[richTextKey] != false,
iconPadding: const EdgeInsets.symmetric( iconPadding: const EdgeInsets.symmetric(
vertical: 14.0, vertical: 14.0,
), ),

View File

@ -20,6 +20,8 @@ class ColorItem extends StatelessWidget {
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
final theme = ToolbarColorExtension.of(context); final theme = ToolbarColorExtension.of(context);
final selectedBackgroundColor = _getBackgroundColor(context);
return MobileToolbarMenuItemWrapper( return MobileToolbarMenuItemWrapper(
size: const Size(82, 52), size: const Size(82, 52),
onTap: () async { onTap: () async {
@ -42,10 +44,11 @@ class ColorItem extends StatelessWidget {
); );
}, },
icon: FlowySvgs.m_aa_color_s, icon: FlowySvgs.m_aa_color_s,
backgroundColor: theme.toolbarMenuItemBackgroundColor, backgroundColor:
isSelected: false, selectedBackgroundColor ?? theme.toolbarMenuItemBackgroundColor,
selectedBackgroundColor: selectedBackgroundColor,
isSelected: selectedBackgroundColor != null,
showRightArrow: true, showRightArrow: true,
enable: editorState.selection?.isCollapsed == false,
iconPadding: const EdgeInsets.only( iconPadding: const EdgeInsets.only(
top: 14.0, top: 14.0,
bottom: 14.0, bottom: 14.0,
@ -53,4 +56,33 @@ class ColorItem extends StatelessWidget {
), ),
); );
} }
Color? _getBackgroundColor(BuildContext context) {
final selection = editorState.selection;
if (selection == null) {
return null;
}
String? backgroundColor =
editorState.toggledStyle[AppFlowyRichTextKeys.backgroundColor];
if (backgroundColor == null) {
if (selection.isCollapsed && selection.startIndex != 0) {
backgroundColor = editorState.getDeltaAttributeValueInSelection<String>(
AppFlowyRichTextKeys.backgroundColor,
selection.copyWith(
start: selection.start.copyWith(
offset: selection.startIndex - 1,
),
),
);
} else {
backgroundColor = editorState.getDeltaAttributeValueInSelection<String>(
AppFlowyRichTextKeys.backgroundColor,
);
}
}
if (backgroundColor != null && int.tryParse(backgroundColor) != null) {
return Color(int.parse(backgroundColor));
}
return null;
}
} }

View File

@ -63,16 +63,9 @@ class _TextColorAndBackgroundColorState
extends State<_TextColorAndBackgroundColor> { extends State<_TextColorAndBackgroundColor> {
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
final String? selectedTextColor = final String? selectedTextColor = _getColor(AppFlowyRichTextKeys.textColor);
widget.editorState.getDeltaAttributeValueInSelection(
AppFlowyRichTextKeys.textColor,
widget.selection,
);
final String? selectedBackgroundColor = final String? selectedBackgroundColor =
widget.editorState.getDeltaAttributeValueInSelection( _getColor(AppFlowyRichTextKeys.backgroundColor);
AppFlowyRichTextKeys.backgroundColor,
widget.selection,
);
return Column( return Column(
crossAxisAlignment: CrossAxisAlignment.start, crossAxisAlignment: CrossAxisAlignment.start,
children: [ children: [
@ -90,17 +83,25 @@ class _TextColorAndBackgroundColorState
selectedColor: selectedTextColor?.tryToColor(), selectedColor: selectedTextColor?.tryToColor(),
onSelectedColor: (textColor) async { onSelectedColor: (textColor) async {
final hex = textColor.alpha == 0 ? null : textColor.toHex(); final hex = textColor.alpha == 0 ? null : textColor.toHex();
await widget.editorState.formatDelta( final selection = widget.selection;
widget.selection, if (selection.isCollapsed) {
{ widget.editorState.updateToggledStyle(
AppFlowyRichTextKeys.textColor: hex, AppFlowyRichTextKeys.textColor,
}, hex ?? '',
selectionExtraInfo: { );
selectionExtraInfoDisableFloatingToolbar: true, } else {
selectionExtraInfoDisableMobileToolbarKey: true, await widget.editorState.formatDelta(
selectionExtraInfoDoNotAttachTextService: true, widget.selection,
}, {
); AppFlowyRichTextKeys.textColor: hex,
},
selectionExtraInfo: {
selectionExtraInfoDisableFloatingToolbar: true,
selectionExtraInfoDisableMobileToolbarKey: true,
selectionExtraInfoDoNotAttachTextService: true,
},
);
}
setState(() {}); setState(() {});
}, },
), ),
@ -119,23 +120,53 @@ class _TextColorAndBackgroundColorState
onSelectedColor: (backgroundColor) async { onSelectedColor: (backgroundColor) async {
final hex = final hex =
backgroundColor.alpha == 0 ? null : backgroundColor.toHex(); backgroundColor.alpha == 0 ? null : backgroundColor.toHex();
await widget.editorState.formatDelta( final selection = widget.selection;
widget.selection, if (selection.isCollapsed) {
{ widget.editorState.updateToggledStyle(
AppFlowyRichTextKeys.backgroundColor: hex, AppFlowyRichTextKeys.backgroundColor,
}, hex ?? '',
selectionExtraInfo: { );
selectionExtraInfoDisableFloatingToolbar: true, } else {
selectionExtraInfoDisableMobileToolbarKey: true, await widget.editorState.formatDelta(
selectionExtraInfoDoNotAttachTextService: true, widget.selection,
}, {
); AppFlowyRichTextKeys.backgroundColor: hex,
},
selectionExtraInfo: {
selectionExtraInfoDisableFloatingToolbar: true,
selectionExtraInfoDisableMobileToolbarKey: true,
selectionExtraInfoDoNotAttachTextService: true,
},
);
}
setState(() {}); setState(() {});
}, },
), ),
], ],
); );
} }
String? _getColor(String key) {
final selection = widget.selection;
String? color = widget.editorState.toggledStyle[key];
if (color == null) {
if (selection.isCollapsed && selection.startIndex != 0) {
color = widget.editorState.getDeltaAttributeValueInSelection<String>(
key,
selection.copyWith(
start: selection.start.copyWith(
offset: selection.startIndex - 1,
),
),
);
} else {
color = widget.editorState.getDeltaAttributeValueInSelection<String>(
key,
);
}
}
return color;
}
} }
class _BackgroundColors extends StatelessWidget { class _BackgroundColors extends StatelessWidget {

View File

@ -1,13 +1,12 @@
import 'dart:async'; import 'dart:async';
import 'package:flutter/material.dart';
import 'package:appflowy/mobile/presentation/setting/font/font_picker_screen.dart'; import 'package:appflowy/mobile/presentation/setting/font/font_picker_screen.dart';
import 'package:appflowy/plugins/document/application/document_appearance_cubit.dart'; import 'package:appflowy/plugins/document/application/document_appearance_cubit.dart';
import 'package:appflowy/plugins/document/presentation/editor_plugins/mobile_toolbar_v3/_toolbar_theme.dart'; import 'package:appflowy/plugins/document/presentation/editor_plugins/mobile_toolbar_v3/_toolbar_theme.dart';
import 'package:appflowy/plugins/document/presentation/editor_plugins/plugins.dart'; import 'package:appflowy/plugins/document/presentation/editor_plugins/plugins.dart';
import 'package:appflowy/util/google_font_family_extension.dart'; import 'package:appflowy/util/google_font_family_extension.dart';
import 'package:appflowy_editor/appflowy_editor.dart'; import 'package:appflowy_editor/appflowy_editor.dart';
import 'package:flutter/material.dart';
import 'package:go_router/go_router.dart'; import 'package:go_router/go_router.dart';
import 'package:google_fonts/google_fonts.dart'; import 'package:google_fonts/google_fonts.dart';
import 'package:provider/provider.dart'; import 'package:provider/provider.dart';
@ -23,15 +22,16 @@ class FontFamilyItem extends StatelessWidget {
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
final theme = ToolbarColorExtension.of(context); final theme = ToolbarColorExtension.of(context);
final fontFamily = editorState.getDeltaAttributeValueInSelection<String>( final fontFamily = _getCurrentSelectedFontFamilyName();
AppFlowyRichTextKeys.fontFamily,
);
final systemFonFamily = final systemFonFamily =
context.read<DocumentAppearanceCubit>().state.fontFamily; context.read<DocumentAppearanceCubit>().state.fontFamily;
return MobileToolbarMenuItemWrapper( return MobileToolbarMenuItemWrapper(
size: const Size(144, 52), size: const Size(144, 52),
onTap: () async { onTap: () async {
final selection = editorState.selection; final selection = editorState.selection;
if (selection == null) {
return;
}
// disable the floating toolbar // disable the floating toolbar
unawaited( unawaited(
editorState.updateSelectionWithReason( editorState.updateSelectionWithReason(
@ -46,12 +46,17 @@ class FontFamilyItem extends StatelessWidget {
final newFont = await context final newFont = await context
.read<GoRouter>() .read<GoRouter>()
.push<String>(FontPickerScreen.routeName); .push<String>(FontPickerScreen.routeName);
if (newFont != null && newFont != fontFamily) {
await editorState.formatDelta(selection, { // if the selection is not collapsed, apply the font to the selection.
AppFlowyRichTextKeys.fontFamily: if (newFont != null && !selection.isCollapsed) {
GoogleFonts.getFont(newFont).fontFamily, if (newFont != fontFamily) {
}); await editorState.formatDelta(selection, {
AppFlowyRichTextKeys.fontFamily:
GoogleFonts.getFont(newFont).fontFamily,
});
}
} }
// wait for the font picker screen to be dismissed. // wait for the font picker screen to be dismissed.
Future.delayed(const Duration(milliseconds: 250), () { Future.delayed(const Duration(milliseconds: 250), () {
// highlight the selected text again. // highlight the selected text again.
@ -62,13 +67,20 @@ class FontFamilyItem extends StatelessWidget {
selectionExtraInfoDisableMobileToolbarKey: false, selectionExtraInfoDisableMobileToolbarKey: false,
}, },
); );
// if the selection is collapsed, save the font for the next typing.
if (newFont != null && selection.isCollapsed) {
editorState.updateToggledStyle(
AppFlowyRichTextKeys.fontFamily,
GoogleFonts.getFont(newFont).fontFamily,
);
}
}); });
}, },
text: (fontFamily ?? systemFonFamily).parseFontFamilyName(), text: (fontFamily ?? systemFonFamily).parseFontFamilyName(),
fontFamily: fontFamily ?? systemFonFamily, fontFamily: fontFamily ?? systemFonFamily,
backgroundColor: theme.toolbarMenuItemBackgroundColor, backgroundColor: theme.toolbarMenuItemBackgroundColor,
isSelected: false, isSelected: false,
enable: editorState.selection?.isCollapsed == false, enable: true,
showRightArrow: true, showRightArrow: true,
iconPadding: const EdgeInsets.only( iconPadding: const EdgeInsets.only(
top: 14.0, top: 14.0,
@ -81,4 +93,28 @@ class FontFamilyItem extends StatelessWidget {
), ),
); );
} }
String? _getCurrentSelectedFontFamilyName() {
final toggleFontFamily =
editorState.toggledStyle[AppFlowyRichTextKeys.fontFamily];
if (toggleFontFamily is String && toggleFontFamily.isNotEmpty) {
return toggleFontFamily;
}
final selection = editorState.selection;
if (selection != null &&
selection.isCollapsed &&
selection.startIndex != 0) {
return editorState.getDeltaAttributeValueInSelection<String>(
AppFlowyRichTextKeys.fontFamily,
selection.copyWith(
start: selection.start.copyWith(
offset: selection.startIndex - 1,
),
),
);
}
return editorState.getDeltaAttributeValueInSelection<String>(
AppFlowyRichTextKeys.fontFamily,
);
}
} }

View File

@ -11,6 +11,7 @@ class MobileToolbarMenuItemWrapper extends StatelessWidget {
this.icon, this.icon,
this.text, this.text,
this.backgroundColor, this.backgroundColor,
this.selectedBackgroundColor,
this.enable, this.enable,
this.fontFamily, this.fontFamily,
required this.isSelected, required this.isSelected,
@ -40,6 +41,7 @@ class MobileToolbarMenuItemWrapper extends StatelessWidget {
final bool showDownArrow; final bool showDownArrow;
final bool showRightArrow; final bool showRightArrow;
final Color? backgroundColor; final Color? backgroundColor;
final Color? selectedBackgroundColor;
final EdgeInsets textPadding; final EdgeInsets textPadding;
@override @override
@ -90,7 +92,8 @@ class MobileToolbarMenuItemWrapper extends StatelessWidget {
alignment: text != null ? Alignment.centerLeft : Alignment.center, alignment: text != null ? Alignment.centerLeft : Alignment.center,
decoration: BoxDecoration( decoration: BoxDecoration(
color: isSelected color: isSelected
? theme.toolbarMenuItemSelectedBackgroundColor ? (selectedBackgroundColor ??
theme.toolbarMenuItemSelectedBackgroundColor)
: backgroundColor, : backgroundColor,
borderRadius: BorderRadius.only( borderRadius: BorderRadius.only(
topLeft: enableTopLeftRadius ? radius : Radius.zero, topLeft: enableTopLeftRadius ? radius : Radius.zero,