diff --git a/core/lib/presentation/views/button/icon_button_web.dart b/core/lib/presentation/views/button/icon_button_web.dart index e5d8c8e2e..9c01146b8 100644 --- a/core/lib/presentation/views/button/icon_button_web.dart +++ b/core/lib/presentation/views/button/icon_button_web.dart @@ -158,4 +158,40 @@ Widget buildButtonWrapText(String name, { color: Colors.white)), ), ); +} +Widget buildButtonWrapText(String name, { + TextStyle? textStyle, + Color? bgColor, + Color? borderColor, + double? radius, + double? height, + EdgeInsets? padding, + IconWebCallback? onTap +}) { + return Container( + height: height ?? 40, + padding: padding, + child: ElevatedButton( + onPressed: () => onTap?.call(), + style: ButtonStyle( + backgroundColor: MaterialStateProperty.resolveWith( + (Set states) => bgColor ?? AppColor.colorTextButton), + shape: MaterialStateProperty.all(RoundedRectangleBorder( + borderRadius: BorderRadius.circular(radius ?? 8), + side: BorderSide( + width: borderColor != null ? 1 : 0, + color: borderColor ?? bgColor ?? AppColor.colorTextButton))), + padding: MaterialStateProperty.resolveWith( + (Set states) => const EdgeInsets.symmetric(horizontal: 16)), + elevation: MaterialStateProperty.resolveWith( + (Set states) => 0)), + child: Text(name, + textAlign: TextAlign.center, + style: textStyle ?? + const TextStyle( + fontSize: 17, + fontWeight: FontWeight.w500, + color: Colors.white)), + ), + ); } \ No newline at end of file diff --git a/lib/features/composer/presentation/composer_view_web.dart b/lib/features/composer/presentation/composer_view_web.dart index eb600799e..be1566f62 100644 --- a/lib/features/composer/presentation/composer_view_web.dart +++ b/lib/features/composer/presentation/composer_view_web.dart @@ -65,7 +65,7 @@ class ComposerView extends GetWidget Expanded(child: Column( children: [ _buildAttachmentsWidget(context), - _buildToolbarRichTextWidget(), + _buildToolbarRichTextWidget(context), _buildInlineLoadingView(), _buildEditorForm(context) ] @@ -299,7 +299,7 @@ class ComposerView extends GetWidget child: Column( children: [ _buildAttachmentsWidget(context), - _buildToolbarRichTextWidget(), + _buildToolbarRichTextWidget(context), _buildInlineLoadingView(), _buildEditorForm(context) ] @@ -734,15 +734,25 @@ class ComposerView extends GetWidget } } - Widget _buildToolbarRichTextWidget() { + Widget _buildToolbarRichTextWidget(BuildContext context) { return Padding( padding: const EdgeInsets.only(left: 20, top: 4, bottom: 8), child: Row( children: RichTextStyleType.values.map((textType) => Obx(() { - return buildIconStyleText( - path: textType.getIcon(imagePaths), - isSelected: controller.richTextWebController.isTextStyleTypeSelected(textType), - onTap: () => controller.richTextWebController.applyRichTextStyle(textType)); + switch(textType) { + case RichTextStyleType.textColor: + return buildIconColorText( + iconData: textType.getIconData(), + colorSelected: controller.richTextWebController.selectedTextColor.value, + tooltip: textType.getTooltipButton(context), + onTap: () => controller.richTextWebController.applyRichTextStyle(context, textType)); + default: + return buildIconStyleText( + path: textType.getIcon(imagePaths), + isSelected: controller.richTextWebController.isTextStyleTypeSelected(textType), + tooltip: textType.getTooltipButton(context), + onTap: () => controller.richTextWebController.applyRichTextStyle(context, textType)); + } })).toList() ), ); diff --git a/lib/features/composer/presentation/controller/base_rich_text_controller.dart b/lib/features/composer/presentation/controller/base_rich_text_controller.dart new file mode 100644 index 000000000..682608393 --- /dev/null +++ b/lib/features/composer/presentation/controller/base_rich_text_controller.dart @@ -0,0 +1,29 @@ + +import 'package:core/presentation/views/dialog/color_picker_dialog_builder.dart'; +import 'package:flutter/material.dart'; +import 'package:get/get.dart'; +import 'package:tmail_ui_user/main/localizations/app_localizations.dart'; +import 'package:tmail_ui_user/main/routes/route_navigation.dart'; + +abstract class BaseRichTextController extends GetxController { + + void openMenuSelectColor( + BuildContext context, + Color currentColor, + {Function(Color?)? onSelectColor} + ) async { + await ColorPickerDialogBuilder( + context, + currentColor, + title: AppLocalizations.of(context).chooseAColor, + textActionSetColor: AppLocalizations.of(context).setColor, + textActionResetDefault: AppLocalizations.of(context).resetToDefault, + textActionCancel: AppLocalizations.of(context).cancel, + cancelActionCallback: () => popBack(), + setColorActionCallback: (selectedColor) { + onSelectColor?.call(selectedColor); + popBack(); + } + ).show(); + } +} \ No newline at end of file diff --git a/lib/features/composer/presentation/controller/rich_text_web_controller.dart b/lib/features/composer/presentation/controller/rich_text_web_controller.dart index c67cea735..45d468f29 100644 --- a/lib/features/composer/presentation/controller/rich_text_web_controller.dart +++ b/lib/features/composer/presentation/controller/rich_text_web_controller.dart @@ -4,18 +4,22 @@ import 'package:dartz/dartz.dart'; import 'package:html/parser.dart' show parse; import 'package:core/utils/app_logger.dart'; +import 'package:flutter/material.dart'; +import 'package:flutter/widgets.dart'; import 'package:get/get.dart'; import 'package:html_editor_enhanced/html_editor.dart'; import 'package:model/email/attachment.dart'; import 'package:tmail_ui_user/features/composer/presentation/model/image_source.dart'; import 'package:tmail_ui_user/features/composer/presentation/model/inline_image.dart'; +import 'package:tmail_ui_user/features/composer/presentation/controller/base_rich_text_controller.dart'; import 'package:tmail_ui_user/features/composer/presentation/model/rich_text_style_type.dart'; -class RichTextWebController extends GetxController { +class RichTextWebController extends BaseRichTextController { final editorController = HtmlEditorController(processNewLineAsBr: true); final listTextStyleApply = RxList(); + final selectedTextColor = Colors.black.obs; void onEditorSettingsChange(EditorSettings settings) { log('RichTextWebController::onEditorSettingsChange():'); @@ -37,9 +41,32 @@ class RichTextWebController extends GetxController { } } - void applyRichTextStyle(RichTextStyleType textStyleType) { - editorController.execCommand(textStyleType.commandAction); - _selectTextStyleType(textStyleType); + void applyRichTextStyle(BuildContext context, RichTextStyleType textStyleType) { + switch(textStyleType) { + case RichTextStyleType.textColor: + openMenuSelectColor( + context, + selectedTextColor.value, + onSelectColor: (selectedColor) { + final newColor = selectedColor ?? Colors.black; + final colorAsString = (newColor.value & 0xFFFFFF) + .toRadixString(16) + .padLeft(6, '0') + .toUpperCase(); + log('RichTextWebController::applyRichTextStyle(): color: $newColor'); + log('RichTextWebController::applyRichTextStyle(): colorAsString: $colorAsString'); + selectedTextColor.value = newColor; + editorController.execCommand( + textStyleType.commandAction, + argument: colorAsString); + } + ); + break; + default: + editorController.execCommand(textStyleType.commandAction); + _selectTextStyleType(textStyleType); + break; + } } void _selectTextStyleType(RichTextStyleType textStyleType) { diff --git a/lib/features/composer/presentation/mixin/rich_text_button_mixin.dart b/lib/features/composer/presentation/mixin/rich_text_button_mixin.dart index 24ebda281..a286496a5 100644 --- a/lib/features/composer/presentation/mixin/rich_text_button_mixin.dart +++ b/lib/features/composer/presentation/mixin/rich_text_button_mixin.dart @@ -10,6 +10,7 @@ mixin RichTextButtonMixin { required String path, required bool? isSelected, required VoidCallback onTap, + String? tooltip }){ return buildIconWeb( icon: SvgPicture.asset( @@ -21,6 +22,23 @@ mixin RichTextButtonMixin { iconPadding: const EdgeInsets.all(4), colorFocus: Colors.white, minSize: 26, + tooltip: tooltip, + onTap: onTap, + ); + } + + Widget buildIconColorText({ + required IconData? iconData, + required Color? colorSelected, + required VoidCallback onTap, + String? tooltip + }){ + return buildIconWeb( + icon: Icon(iconData, color: colorSelected ?? Colors.black, size: 20), + iconPadding: const EdgeInsets.all(4), + colorFocus: Colors.white, + minSize: 20, + tooltip: tooltip, onTap: onTap, ); } diff --git a/lib/features/composer/presentation/model/rich_text_style_type.dart b/lib/features/composer/presentation/model/rich_text_style_type.dart index 4d6d9961e..ba76c5fe7 100644 --- a/lib/features/composer/presentation/model/rich_text_style_type.dart +++ b/lib/features/composer/presentation/model/rich_text_style_type.dart @@ -1,11 +1,14 @@ import 'package:core/presentation/resources/image_paths.dart'; +import 'package:flutter/material.dart'; +import 'package:tmail_ui_user/main/localizations/app_localizations.dart'; enum RichTextStyleType { bold, italic, underline, - strikeThrough; + strikeThrough, + textColor; String get commandAction { switch (this) { @@ -17,6 +20,8 @@ enum RichTextStyleType { return 'underline'; case strikeThrough: return 'strikeThrough'; + case textColor: + return 'foreColor'; default: return ''; } @@ -36,4 +41,30 @@ enum RichTextStyleType { return ''; } } + + IconData? getIconData() { + switch (this) { + case textColor: + return Icons.format_color_text; + default: + return null; + } + } + + String getTooltipButton(BuildContext context) { + switch (this) { + case bold: + return AppLocalizations.of(context).formatBold; + case italic: + return AppLocalizations.of(context).formatItalic; + case underline: + return AppLocalizations.of(context).formatUnderline; + case strikeThrough: + return AppLocalizations.of(context).formatStrikethrough; + case textColor: + return AppLocalizations.of(context).formatTextColor; + default: + return ''; + } + } } \ No newline at end of file diff --git a/lib/l10n/intl_messages.arb b/lib/l10n/intl_messages.arb index c2a2645de..7df84a3a8 100644 --- a/lib/l10n/intl_messages.arb +++ b/lib/l10n/intl_messages.arb @@ -1,5 +1,5 @@ { - "@@last_modified": "2022-07-18T16:31:40.598846", + "@@last_modified": "2022-07-21T13:16:06.832687", "initializing_data": "Initializing data...", "@initializing_data": { "type": "text", @@ -1615,5 +1615,53 @@ "type": "text", "placeholders_order": [], "placeholders": {} + }, + "chooseAColor": "Choose a color", + "@chooseAColor": { + "type": "text", + "placeholders_order": [], + "placeholders": {} + }, + "resetToDefault": "Reset to default", + "@resetToDefault": { + "type": "text", + "placeholders_order": [], + "placeholders": {} + }, + "setColor": "Set color", + "@setColor": { + "type": "text", + "placeholders_order": [], + "placeholders": {} + }, + "formatBold": "Bold", + "@formatBold": { + "type": "text", + "placeholders_order": [], + "placeholders": {} + }, + "formatItalic": "Italic", + "@formatItalic": { + "type": "text", + "placeholders_order": [], + "placeholders": {} + }, + "formatUnderline": "Underline", + "@formatUnderline": { + "type": "text", + "placeholders_order": [], + "placeholders": {} + }, + "formatStrikethrough": "Strikethrough", + "@formatStrikethrough": { + "type": "text", + "placeholders_order": [], + "placeholders": {} + }, + "formatTextColor": "Text Color", + "@formatTextColor": { + "type": "text", + "placeholders_order": [], + "placeholders": {} } } \ No newline at end of file diff --git a/lib/main/localizations/app_localizations.dart b/lib/main/localizations/app_localizations.dart index bac8f55a5..7404868f0 100644 --- a/lib/main/localizations/app_localizations.dart +++ b/lib/main/localizations/app_localizations.dart @@ -1659,4 +1659,52 @@ class AppLocalizations { 'Please input either an image or an image URL, not both', name: 'insertImageErrorDuplicate'); } + + String get chooseAColor { + return Intl.message( + 'Choose a color', + name: 'chooseAColor'); + } + + String get resetToDefault { + return Intl.message( + 'Reset to default', + name: 'resetToDefault'); + } + + String get setColor { + return Intl.message( + 'Set color', + name: 'setColor'); + } + + String get formatBold { + return Intl.message( + 'Bold', + name: 'formatBold'); + } + + String get formatItalic { + return Intl.message( + 'Italic', + name: 'formatItalic'); + } + + String get formatUnderline { + return Intl.message( + 'Underline', + name: 'formatUnderline'); + } + + String get formatStrikethrough { + return Intl.message( + 'Strikethrough', + name: 'formatStrikethrough'); + } + + String get formatTextColor { + return Intl.message( + 'Text Color', + name: 'formatTextColor'); + } } \ No newline at end of file