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,
isSelected: editorState.isTextDecorationSelected(richTextKey) &&
editorState.toggledStyle[richTextKey] != true,
editorState.toggledStyle[richTextKey] != false,
iconPadding: const EdgeInsets.symmetric(
vertical: 14.0,
),

View File

@ -20,6 +20,8 @@ class ColorItem extends StatelessWidget {
@override
Widget build(BuildContext context) {
final theme = ToolbarColorExtension.of(context);
final selectedBackgroundColor = _getBackgroundColor(context);
return MobileToolbarMenuItemWrapper(
size: const Size(82, 52),
onTap: () async {
@ -42,10 +44,11 @@ class ColorItem extends StatelessWidget {
);
},
icon: FlowySvgs.m_aa_color_s,
backgroundColor: theme.toolbarMenuItemBackgroundColor,
isSelected: false,
backgroundColor:
selectedBackgroundColor ?? theme.toolbarMenuItemBackgroundColor,
selectedBackgroundColor: selectedBackgroundColor,
isSelected: selectedBackgroundColor != null,
showRightArrow: true,
enable: editorState.selection?.isCollapsed == false,
iconPadding: const EdgeInsets.only(
top: 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> {
@override
Widget build(BuildContext context) {
final String? selectedTextColor =
widget.editorState.getDeltaAttributeValueInSelection(
AppFlowyRichTextKeys.textColor,
widget.selection,
);
final String? selectedTextColor = _getColor(AppFlowyRichTextKeys.textColor);
final String? selectedBackgroundColor =
widget.editorState.getDeltaAttributeValueInSelection(
AppFlowyRichTextKeys.backgroundColor,
widget.selection,
);
_getColor(AppFlowyRichTextKeys.backgroundColor);
return Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
@ -90,17 +83,25 @@ class _TextColorAndBackgroundColorState
selectedColor: selectedTextColor?.tryToColor(),
onSelectedColor: (textColor) async {
final hex = textColor.alpha == 0 ? null : textColor.toHex();
await widget.editorState.formatDelta(
widget.selection,
{
AppFlowyRichTextKeys.textColor: hex,
},
selectionExtraInfo: {
selectionExtraInfoDisableFloatingToolbar: true,
selectionExtraInfoDisableMobileToolbarKey: true,
selectionExtraInfoDoNotAttachTextService: true,
},
);
final selection = widget.selection;
if (selection.isCollapsed) {
widget.editorState.updateToggledStyle(
AppFlowyRichTextKeys.textColor,
hex ?? '',
);
} else {
await widget.editorState.formatDelta(
widget.selection,
{
AppFlowyRichTextKeys.textColor: hex,
},
selectionExtraInfo: {
selectionExtraInfoDisableFloatingToolbar: true,
selectionExtraInfoDisableMobileToolbarKey: true,
selectionExtraInfoDoNotAttachTextService: true,
},
);
}
setState(() {});
},
),
@ -119,23 +120,53 @@ class _TextColorAndBackgroundColorState
onSelectedColor: (backgroundColor) async {
final hex =
backgroundColor.alpha == 0 ? null : backgroundColor.toHex();
await widget.editorState.formatDelta(
widget.selection,
{
AppFlowyRichTextKeys.backgroundColor: hex,
},
selectionExtraInfo: {
selectionExtraInfoDisableFloatingToolbar: true,
selectionExtraInfoDisableMobileToolbarKey: true,
selectionExtraInfoDoNotAttachTextService: true,
},
);
final selection = widget.selection;
if (selection.isCollapsed) {
widget.editorState.updateToggledStyle(
AppFlowyRichTextKeys.backgroundColor,
hex ?? '',
);
} else {
await widget.editorState.formatDelta(
widget.selection,
{
AppFlowyRichTextKeys.backgroundColor: hex,
},
selectionExtraInfo: {
selectionExtraInfoDisableFloatingToolbar: true,
selectionExtraInfoDisableMobileToolbarKey: true,
selectionExtraInfoDoNotAttachTextService: true,
},
);
}
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 {

View File

@ -1,13 +1,12 @@
import 'dart:async';
import 'package:flutter/material.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/presentation/editor_plugins/mobile_toolbar_v3/_toolbar_theme.dart';
import 'package:appflowy/plugins/document/presentation/editor_plugins/plugins.dart';
import 'package:appflowy/util/google_font_family_extension.dart';
import 'package:appflowy_editor/appflowy_editor.dart';
import 'package:flutter/material.dart';
import 'package:go_router/go_router.dart';
import 'package:google_fonts/google_fonts.dart';
import 'package:provider/provider.dart';
@ -23,15 +22,16 @@ class FontFamilyItem extends StatelessWidget {
@override
Widget build(BuildContext context) {
final theme = ToolbarColorExtension.of(context);
final fontFamily = editorState.getDeltaAttributeValueInSelection<String>(
AppFlowyRichTextKeys.fontFamily,
);
final fontFamily = _getCurrentSelectedFontFamilyName();
final systemFonFamily =
context.read<DocumentAppearanceCubit>().state.fontFamily;
return MobileToolbarMenuItemWrapper(
size: const Size(144, 52),
onTap: () async {
final selection = editorState.selection;
if (selection == null) {
return;
}
// disable the floating toolbar
unawaited(
editorState.updateSelectionWithReason(
@ -46,12 +46,17 @@ class FontFamilyItem extends StatelessWidget {
final newFont = await context
.read<GoRouter>()
.push<String>(FontPickerScreen.routeName);
if (newFont != null && newFont != fontFamily) {
await editorState.formatDelta(selection, {
AppFlowyRichTextKeys.fontFamily:
GoogleFonts.getFont(newFont).fontFamily,
});
// if the selection is not collapsed, apply the font to the selection.
if (newFont != null && !selection.isCollapsed) {
if (newFont != fontFamily) {
await editorState.formatDelta(selection, {
AppFlowyRichTextKeys.fontFamily:
GoogleFonts.getFont(newFont).fontFamily,
});
}
}
// wait for the font picker screen to be dismissed.
Future.delayed(const Duration(milliseconds: 250), () {
// highlight the selected text again.
@ -62,13 +67,20 @@ class FontFamilyItem extends StatelessWidget {
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(),
fontFamily: fontFamily ?? systemFonFamily,
backgroundColor: theme.toolbarMenuItemBackgroundColor,
isSelected: false,
enable: editorState.selection?.isCollapsed == false,
enable: true,
showRightArrow: true,
iconPadding: const EdgeInsets.only(
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.text,
this.backgroundColor,
this.selectedBackgroundColor,
this.enable,
this.fontFamily,
required this.isSelected,
@ -40,6 +41,7 @@ class MobileToolbarMenuItemWrapper extends StatelessWidget {
final bool showDownArrow;
final bool showRightArrow;
final Color? backgroundColor;
final Color? selectedBackgroundColor;
final EdgeInsets textPadding;
@override
@ -90,7 +92,8 @@ class MobileToolbarMenuItemWrapper extends StatelessWidget {
alignment: text != null ? Alignment.centerLeft : Alignment.center,
decoration: BoxDecoration(
color: isSelected
? theme.toolbarMenuItemSelectedBackgroundColor
? (selectedBackgroundColor ??
theme.toolbarMenuItemSelectedBackgroundColor)
: backgroundColor,
borderRadius: BorderRadius.only(
topLeft: enableTopLeftRadius ? radius : Radius.zero,