mirror of
https://github.com/AppFlowy-IO/AppFlowy.git
synced 2024-08-30 18:12:39 +00:00
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:
parent
e250f780a4
commit
abd08e5e53
@ -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,
|
||||||
),
|
),
|
||||||
|
@ -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;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -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 {
|
||||||
|
@ -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,
|
||||||
|
);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -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,
|
||||||
|
Loading…
Reference in New Issue
Block a user