diff --git a/assets/images/ic_align_center.svg b/assets/images/ic_align_center.svg
new file mode 100644
index 000000000..4326d282e
--- /dev/null
+++ b/assets/images/ic_align_center.svg
@@ -0,0 +1,6 @@
+
diff --git a/assets/images/ic_align_indent.svg b/assets/images/ic_align_indent.svg
new file mode 100644
index 000000000..9f2652ead
--- /dev/null
+++ b/assets/images/ic_align_indent.svg
@@ -0,0 +1,3 @@
+
diff --git a/assets/images/ic_align_justify.svg b/assets/images/ic_align_justify.svg
new file mode 100644
index 000000000..7c7ff2cef
--- /dev/null
+++ b/assets/images/ic_align_justify.svg
@@ -0,0 +1,6 @@
+
diff --git a/assets/images/ic_align_left.svg b/assets/images/ic_align_left.svg
new file mode 100644
index 000000000..af9911ad0
--- /dev/null
+++ b/assets/images/ic_align_left.svg
@@ -0,0 +1,6 @@
+
diff --git a/assets/images/ic_align_outdent.svg b/assets/images/ic_align_outdent.svg
new file mode 100644
index 000000000..1d24c8033
--- /dev/null
+++ b/assets/images/ic_align_outdent.svg
@@ -0,0 +1,3 @@
+
diff --git a/assets/images/ic_align_right.svg b/assets/images/ic_align_right.svg
new file mode 100644
index 000000000..9d07bed55
--- /dev/null
+++ b/assets/images/ic_align_right.svg
@@ -0,0 +1,6 @@
+
diff --git a/core/lib/presentation/resources/image_paths.dart b/core/lib/presentation/resources/image_paths.dart
index 6bc09bbda..5e5884f74 100644
--- a/core/lib/presentation/resources/image_paths.dart
+++ b/core/lib/presentation/resources/image_paths.dart
@@ -137,6 +137,12 @@ class ImagePaths {
String get icStyleHeader => _getImagePath('ic_style_header.svg');
String get icStyleColor => _getImagePath('ic_style_color.svg');
String get icStyleArrowDown => _getImagePath('ic_style_arrow_down.svg');
+ String get icAlignLeft => _getImagePath('ic_align_left.svg');
+ String get icAlignCenter => _getImagePath('ic_align_center.svg');
+ String get icAlignRight => _getImagePath('ic_align_right.svg');
+ String get icAlignJustify => _getImagePath('ic_align_justify.svg');
+ String get icAlignIndent => _getImagePath('ic_align_indent.svg');
+ String get icAlignOutdent => _getImagePath('ic_align_outdent.svg');
String _getImagePath(String imageName) {
return AssetsPaths.images + imageName;
diff --git a/lib/features/base/widget/drop_down_button_widget.dart b/lib/features/base/widget/drop_down_button_widget.dart
index cdf064939..d65f16dad 100644
--- a/lib/features/base/widget/drop_down_button_widget.dart
+++ b/lib/features/base/widget/drop_down_button_widget.dart
@@ -26,6 +26,7 @@ class DropDownButtonWidget extends StatelessWidget {
final double opacity;
final Widget? iconArrowDown;
final Color? colorButton;
+ final String tooltip;
const DropDownButtonWidget({
Key? key,
@@ -41,6 +42,7 @@ class DropDownButtonWidget extends StatelessWidget {
this.opacity = 1.0,
this.iconArrowDown,
this.colorButton = Colors.white,
+ this.tooltip = '',
}) : super(key: key);
@override
@@ -91,28 +93,31 @@ class DropDownButtonWidget extends StatelessWidget {
.toList(),
value: itemSelected,
customButton: supportSelectionIcon
- ? Container(
- height: heightItem,
- decoration: BoxDecoration(
- borderRadius: BorderRadius.circular(radiusButton),
- border: Border.all(
- color: AppColor.colorInputBorderCreateMailbox,
- width: 0.5,
+ ? Tooltip(
+ message: tooltip,
+ child: Container(
+ height: heightItem,
+ decoration: BoxDecoration(
+ borderRadius: BorderRadius.circular(radiusButton),
+ border: Border.all(
+ color: AppColor.colorInputBorderCreateMailbox,
+ width: 0.5,
+ ),
+ color: colorButton ?? AppColor.colorInputBackgroundCreateMailbox,
),
- color: colorButton ?? AppColor.colorInputBackgroundCreateMailbox,
+ padding: const EdgeInsets.only(left: 12, right: 10),
+ child: Row(children: [
+ Expanded(child: Text(
+ _getTextItemDropdown(context, item: itemSelected),
+ style: TextStyle(fontSize: 16,
+ fontWeight: FontWeight.normal,
+ color: Colors.black.withOpacity(opacity)),
+ maxLines: 1,
+ overflow: CommonTextStyle.defaultTextOverFlow,
+ )),
+ iconArrowDown ?? SvgPicture.asset(_imagePaths.icDropDown)
+ ]),
),
- padding: const EdgeInsets.only(left: 12, right: 10),
- child: Row(children: [
- Expanded(child: Text(
- _getTextItemDropdown(context, item: itemSelected),
- style: TextStyle(fontSize: 16,
- fontWeight: FontWeight.normal,
- color: Colors.black.withOpacity(opacity)),
- maxLines: 1,
- overflow: CommonTextStyle.defaultTextOverFlow,
- )),
- iconArrowDown ?? SvgPicture.asset(_imagePaths.icDropDown)
- ]),
)
: null,
onChanged: onChanged,
diff --git a/lib/features/composer/presentation/composer_view_web.dart b/lib/features/composer/presentation/composer_view_web.dart
index 7256c4759..8b6dcf91c 100644
--- a/lib/features/composer/presentation/composer_view_web.dart
+++ b/lib/features/composer/presentation/composer_view_web.dart
@@ -10,6 +10,7 @@ import 'package:jmap_dart_client/jmap/identities/identity.dart';
import 'package:model/model.dart';
import 'package:pointer_interceptor/pointer_interceptor.dart';
import 'package:tmail_ui_user/features/base/mixin/app_loader_mixin.dart';
+import 'package:tmail_ui_user/features/base/widget/popup_menu_overlay_widget.dart';
import 'package:tmail_ui_user/features/composer/domain/state/download_image_as_base64_state.dart';
import 'package:tmail_ui_user/features/base/widget/drop_down_button_widget.dart';
import 'package:tmail_ui_user/features/composer/presentation/composer_controller.dart';
@@ -17,6 +18,7 @@ import 'package:tmail_ui_user/features/composer/presentation/mixin/rich_text_but
import 'package:tmail_ui_user/features/composer/presentation/model/dropdown_menu_font_status.dart';
import 'package:tmail_ui_user/features/composer/presentation/model/font_name_type.dart';
import 'package:tmail_ui_user/features/composer/presentation/model/header_style_type.dart';
+import 'package:tmail_ui_user/features/composer/presentation/model/paragraph_type.dart';
import 'package:tmail_ui_user/features/composer/presentation/model/rich_text_style_type.dart';
import 'package:tmail_ui_user/features/composer/presentation/model/screen_display_mode.dart';
import 'package:tmail_ui_user/features/composer/presentation/widgets/attachment_file_composer_builder.dart';
@@ -624,12 +626,7 @@ class ComposerView extends GetWidget
Future.delayed(const Duration(milliseconds: 500), () {
controller.richTextWebController.editorController.setFocus();
});
- if (controller.richTextWebController.isMenuFontOpen) {
- controller.richTextWebController.closeDropdownMenuFont();
- }
- if (controller.richTextWebController.isMenuHeaderStyleOpen) {
- controller.richTextWebController.closeDropdownMenuHeaderStyle();
- }
+ controller.richTextWebController.closeAllMenuPopup();
}, onBlur: () {
controller.onEditorFocusChange(false);
}, onMouseDown: () {
@@ -766,19 +763,19 @@ class ComposerView extends GetWidget
padding: const EdgeInsets.only(left: 20, top: 8, bottom: 8),
alignment: Alignment.centerLeft,
child: Wrap(
- alignment: WrapAlignment.start,
crossAxisAlignment: WrapCrossAlignment.center,
+ runSpacing: 8,
children: [
AbsorbPointer(
absorbing: codeViewEnabled,
child: DropDownMenuHeaderStyleWidget(
icon: buildWrapIconStyleText(
- icon: Tooltip(
- message: RichTextStyleType.headerStyle.getTooltipButton(context),
- child: SvgPicture.asset(RichTextStyleType.headerStyle.getIcon(imagePaths),
- color: AppColor.colorDefaultRichTextButton.withOpacity(opacity),
- fit: BoxFit.fill),
- )
+ isSelected: richTextController.isMenuHeaderStyleOpen,
+ icon: SvgPicture.asset(RichTextStyleType.headerStyle.getIcon(imagePaths),
+ color: AppColor.colorDefaultRichTextButton.withOpacity(opacity),
+ fit: BoxFit.fill),
+ padding: const EdgeInsets.symmetric(vertical: 5, horizontal: 5),
+ tooltip: RichTextStyleType.headerStyle.getTooltipButton(context)
),
items: HeaderStyleType.values,
onMenuStateChange: (isOpen) {
@@ -786,7 +783,7 @@ class ComposerView extends GetWidget
final newStatus = isOpen
? DropdownMenuFontStatus.open
: DropdownMenuFontStatus.closed;
- richTextController.menuHeaderStyleStatus = newStatus;
+ richTextController.menuHeaderStyleStatus.value = newStatus;
},
onChanged: (newStyle) => richTextController.applyHeaderStyle(newStyle)),
),
@@ -804,32 +801,39 @@ class ComposerView extends GetWidget
final newStatus = isOpen
? DropdownMenuFontStatus.open
: DropdownMenuFontStatus.closed;
- richTextController.menuFontStatus = newStatus;
+ richTextController.menuFontStatus.value = newStatus;
},
- heightItem: 38,
+ heightItem: 40,
sizeIconChecked: 16,
- radiusButton: 5,
+ radiusButton: 8,
opacity: opacity,
- colorButton: Colors.white,
+ colorButton: richTextController.isMenuFontOpen
+ ? AppColor.colorBackgroundWrapIconStyleCode
+ : Colors.white,
iconArrowDown: SvgPicture.asset(imagePaths.icStyleArrowDown),
+ tooltip: RichTextStyleType.fontName.getTooltipButton(context),
supportSelectionIcon: true)),
),
- AbsorbPointer(
- absorbing: codeViewEnabled,
- child: buildWrapIconStyleText(
- icon: buildIconWithTooltip(
- path: RichTextStyleType.textColor.getIcon(imagePaths),
- color: richTextController.selectedTextColor.value,
- tooltip: RichTextStyleType.textColor.getTooltipButton(context),
- opacity: opacity),
- onTap: () => richTextController.applyRichTextStyle(context, RichTextStyleType.textColor)),
- ),
Padding(
- padding: const EdgeInsets.symmetric(horizontal: 8),
+ padding: const EdgeInsets.only(right: 8),
child: AbsorbPointer(
absorbing: codeViewEnabled,
child: buildWrapIconStyleText(
- padding: const EdgeInsets.symmetric(vertical: 8, horizontal: 5),
+ icon: buildIconWithTooltip(
+ path: RichTextStyleType.textColor.getIcon(imagePaths),
+ color: richTextController.selectedTextColor.value,
+ tooltip: RichTextStyleType.textColor.getTooltipButton(context),
+ opacity: opacity),
+ onTap: () => richTextController.applyRichTextStyle(context, RichTextStyleType.textColor)),
+ ),
+ ),
+ Padding(
+ padding: const EdgeInsets.only(right: 8),
+ child: AbsorbPointer(
+ absorbing: codeViewEnabled,
+ child: buildWrapIconStyleText(
+ padding: const EdgeInsets.symmetric(vertical: 9, horizontal: 7),
+ spacing: 3,
icon: buildIconColorBackgroundText(
iconData: RichTextStyleType.textBackgroundColor.getIconData(),
colorSelected: richTextController.selectedTextBackgroundColor.value,
@@ -838,52 +842,79 @@ class ComposerView extends GetWidget
onTap: () => richTextController.applyRichTextStyle(context, RichTextStyleType.textBackgroundColor)),
),
),
- buildWrapIconStyleText(
- hasDropdown: false,
- padding: const EdgeInsets.symmetric(vertical: 3, horizontal: 5),
- icon: Wrap(children: [
- AbsorbPointer(
- absorbing: codeViewEnabled,
- child: buildIconStyleText(
- path: RichTextStyleType.bold.getIcon(imagePaths),
- isSelected: richTextController.isTextStyleTypeSelected(RichTextStyleType.bold),
- tooltip: RichTextStyleType.bold.getTooltipButton(context),
- opacity: opacity,
- onTap: () => richTextController.applyRichTextStyle(context, RichTextStyleType.bold)),
- ),
- AbsorbPointer(
- absorbing: codeViewEnabled,
- child: buildIconStyleText(
- path: RichTextStyleType.italic.getIcon(imagePaths),
- isSelected: richTextController.isTextStyleTypeSelected(RichTextStyleType.italic),
- tooltip: RichTextStyleType.italic.getTooltipButton(context),
- opacity: opacity,
- onTap: () => richTextController.applyRichTextStyle(context, RichTextStyleType.italic)),
- ),
- AbsorbPointer(
- absorbing: codeViewEnabled,
- child: buildIconStyleText(
- path: RichTextStyleType.underline.getIcon(imagePaths),
- isSelected: richTextController.isTextStyleTypeSelected(RichTextStyleType.underline),
- tooltip: RichTextStyleType.underline.getTooltipButton(context),
- opacity: opacity,
- onTap: () => richTextController.applyRichTextStyle(context, RichTextStyleType.underline)),
- ),
- AbsorbPointer(
- absorbing: codeViewEnabled,
- child: buildIconStyleText(
- path: RichTextStyleType.strikeThrough.getIcon(imagePaths),
- isSelected: richTextController.isTextStyleTypeSelected(
- RichTextStyleType.strikeThrough),
- tooltip: RichTextStyleType.strikeThrough.getTooltipButton(context),
- opacity: opacity,
- onTap: () => richTextController.applyRichTextStyle(context, RichTextStyleType.strikeThrough)),
- )
- ])),
Padding(
- padding: const EdgeInsets.symmetric(horizontal: 8),
+ padding: const EdgeInsets.only(right: 8.0),
child: buildWrapIconStyleText(
- padding: const EdgeInsets.symmetric(vertical: 4, horizontal: 5),
+ hasDropdown: false,
+ padding: const EdgeInsets.symmetric(vertical: 3, horizontal: 5),
+ icon: Wrap(children: [
+ AbsorbPointer(
+ absorbing: codeViewEnabled,
+ child: buildIconStyleText(
+ path: RichTextStyleType.bold.getIcon(imagePaths),
+ isSelected: richTextController.isTextStyleTypeSelected(RichTextStyleType.bold),
+ tooltip: RichTextStyleType.bold.getTooltipButton(context),
+ opacity: opacity,
+ onTap: () => richTextController.applyRichTextStyle(context, RichTextStyleType.bold)),
+ ),
+ AbsorbPointer(
+ absorbing: codeViewEnabled,
+ child: buildIconStyleText(
+ path: RichTextStyleType.italic.getIcon(imagePaths),
+ isSelected: richTextController.isTextStyleTypeSelected(RichTextStyleType.italic),
+ tooltip: RichTextStyleType.italic.getTooltipButton(context),
+ opacity: opacity,
+ onTap: () => richTextController.applyRichTextStyle(context, RichTextStyleType.italic)),
+ ),
+ AbsorbPointer(
+ absorbing: codeViewEnabled,
+ child: buildIconStyleText(
+ path: RichTextStyleType.underline.getIcon(imagePaths),
+ isSelected: richTextController.isTextStyleTypeSelected(RichTextStyleType.underline),
+ tooltip: RichTextStyleType.underline.getTooltipButton(context),
+ opacity: opacity,
+ onTap: () => richTextController.applyRichTextStyle(context, RichTextStyleType.underline)),
+ ),
+ AbsorbPointer(
+ absorbing: codeViewEnabled,
+ child: buildIconStyleText(
+ path: RichTextStyleType.strikeThrough.getIcon(imagePaths),
+ isSelected: richTextController.isTextStyleTypeSelected(
+ RichTextStyleType.strikeThrough),
+ tooltip: RichTextStyleType.strikeThrough.getTooltipButton(context),
+ opacity: opacity,
+ onTap: () => richTextController.applyRichTextStyle(context, RichTextStyleType.strikeThrough)),
+ )
+ ])),
+ ),
+ Padding(
+ padding: const EdgeInsets.only(right: 8.0),
+ child: AbsorbPointer(
+ absorbing: codeViewEnabled,
+ child: PopupMenuOverlayWidget(
+ controller: richTextController.menuParagraphController,
+ listButtonAction: ParagraphType.values
+ .map((paragraph) => paragraph.buildButtonWidget(
+ context,
+ imagePaths,
+ (paragraph) => richTextController.applyParagraphType(paragraph)))
+ .toList(),
+ iconButton: buildWrapIconStyleText(
+ padding: const EdgeInsets.symmetric(vertical: 5, horizontal: 5),
+ spacing: 3,
+ opacity: opacity,
+ isSelected: richTextController.focusMenuParagraph.value,
+ icon: buildIconWithTooltip(
+ path: richTextController.selectedParagraph.value.getIcon(imagePaths),
+ color: AppColor.colorDefaultRichTextButton.withOpacity(opacity),
+ tooltip: RichTextStyleType.paragraph.getTooltipButton(context))),
+ ),
+ ),
+ ),
+ Padding(
+ padding: const EdgeInsets.only(right: 8),
+ child: buildWrapIconStyleText(
+ padding: const EdgeInsets.symmetric(vertical: 5, horizontal: 5),
isSelected: richTextController.isTextStyleTypeSelected(RichTextStyleType.codeView),
icon: buildIconWithTooltip(
path: RichTextStyleType.codeView.getIcon(imagePaths),
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 d98a641ac..139e8323d 100644
--- a/lib/features/composer/presentation/mixin/rich_text_button_mixin.dart
+++ b/lib/features/composer/presentation/mixin/rich_text_button_mixin.dart
@@ -15,29 +15,41 @@ mixin RichTextButtonMixin {
bool hasDropdown = true,
double opacity = 1.0,
EdgeInsets? padding,
+ double? spacing,
+ String tooltip = '',
}){
- return InkWell(
- onTap: onTap,
+ final buttonIcon = Tooltip(
+ message: tooltip,
child: Container(
padding: padding ?? const EdgeInsets.symmetric(horizontal: 5, vertical: 5),
decoration: BoxDecoration(
color: isSelected == true
- ? AppColor.colorBackgroundWrapIconStyleCode.withOpacity(opacity)
- : Colors.white.withOpacity(opacity),
+ ? AppColor.colorBackgroundWrapIconStyleCode
+ : Colors.white,
border: Border.all(
color: AppColor.colorBorderWrapIconStyleCode,
width: 0.5),
borderRadius: BorderRadius.circular(8)),
child: hasDropdown
- ? Row(
- mainAxisSize: MainAxisSize.min,
- children: [
- icon,
- SvgPicture.asset(_imagePaths.icStyleArrowDown)
- ])
- : icon,
+ ? Row(
+ mainAxisSize: MainAxisSize.min,
+ children: [
+ icon,
+ if (spacing != null) SizedBox(width: spacing),
+ SvgPicture.asset(_imagePaths.icStyleArrowDown)
+ ])
+ : icon,
),
);
+
+ if (onTap != null) {
+ return InkWell(
+ onTap: onTap,
+ child: buttonIcon,
+ );
+ } else {
+ return buttonIcon;
+ }
}
Widget buildIconStyleText({
diff --git a/lib/l10n/intl_messages.arb b/lib/l10n/intl_messages.arb
index 6fe2fa1d2..e2d6bea9b 100644
--- a/lib/l10n/intl_messages.arb
+++ b/lib/l10n/intl_messages.arb
@@ -1,5 +1,5 @@
{
- "@@last_modified": "2022-07-22T18:31:58.383010",
+ "@@last_modified": "2022-07-26T16:21:05.066657",
"initializing_data": "Initializing data...",
"@initializing_data": {
"type": "text",
@@ -1681,5 +1681,53 @@
"type": "text",
"placeholders_order": [],
"placeholders": {}
+ },
+ "fontFamily": "Font Family",
+ "@fontFamily": {
+ "type": "text",
+ "placeholders_order": [],
+ "placeholders": {}
+ },
+ "paragraph": "Paragraph",
+ "@paragraph": {
+ "type": "text",
+ "placeholders_order": [],
+ "placeholders": {}
+ },
+ "alignLeft": "Align left",
+ "@alignLeft": {
+ "type": "text",
+ "placeholders_order": [],
+ "placeholders": {}
+ },
+ "alignRight": "Align right",
+ "@alignRight": {
+ "type": "text",
+ "placeholders_order": [],
+ "placeholders": {}
+ },
+ "alignCenter": "Align center",
+ "@alignCenter": {
+ "type": "text",
+ "placeholders_order": [],
+ "placeholders": {}
+ },
+ "justifyFull": "Justify full",
+ "@justifyFull": {
+ "type": "text",
+ "placeholders_order": [],
+ "placeholders": {}
+ },
+ "outdent": "Outdent",
+ "@outdent": {
+ "type": "text",
+ "placeholders_order": [],
+ "placeholders": {}
+ },
+ "indent": "Indent",
+ "@indent": {
+ "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 4043d70c3..624527032 100644
--- a/lib/main/localizations/app_localizations.dart
+++ b/lib/main/localizations/app_localizations.dart
@@ -1725,4 +1725,52 @@ class AppLocalizations {
'Style',
name: 'headerStyle');
}
+
+ String get fontFamily {
+ return Intl.message(
+ 'Font Family',
+ name: 'fontFamily');
+ }
+
+ String get paragraph {
+ return Intl.message(
+ 'Paragraph',
+ name: 'paragraph');
+ }
+
+ String get alignLeft {
+ return Intl.message(
+ 'Align left',
+ name: 'alignLeft');
+ }
+
+ String get alignRight {
+ return Intl.message(
+ 'Align right',
+ name: 'alignRight');
+ }
+
+ String get alignCenter {
+ return Intl.message(
+ 'Align center',
+ name: 'alignCenter');
+ }
+
+ String get justifyFull {
+ return Intl.message(
+ 'Justify full',
+ name: 'justifyFull');
+ }
+
+ String get outdent {
+ return Intl.message(
+ 'Outdent',
+ name: 'outdent');
+ }
+
+ String get indent {
+ return Intl.message(
+ 'Indent',
+ name: 'indent');
+ }
}
\ No newline at end of file
diff --git a/pubspec.yaml b/pubspec.yaml
index 6d8ca45b4..92fbbc86d 100644
--- a/pubspec.yaml
+++ b/pubspec.yaml
@@ -174,6 +174,8 @@ dependencies:
# HTML5 parser
html: 0.15.0
+ custom_pop_up_menu: 1.2.4
+
dev_dependencies:
flutter_test:
sdk: flutter