feat: move emoji_picker to appflowy editor plugin
This commit is contained in:
parent
9228c6065e
commit
a3252158ad
11 changed files with 55 additions and 214 deletions
|
|
@ -59,6 +59,8 @@ class SimpleEditor extends StatelessWidget {
|
|||
mathEquationMenuItem,
|
||||
// Code Block
|
||||
codeBlockMenuItem,
|
||||
// Emoji
|
||||
emojiMenuItem,
|
||||
],
|
||||
);
|
||||
} else {
|
||||
|
|
|
|||
|
|
@ -6,10 +6,14 @@
|
|||
|
||||
#include "generated_plugin_registrant.h"
|
||||
|
||||
#include <flowy_infra_ui/flowy_infra_u_i_plugin.h>
|
||||
#include <rich_clipboard_linux/rich_clipboard_plugin.h>
|
||||
#include <url_launcher_linux/url_launcher_plugin.h>
|
||||
|
||||
void fl_register_plugins(FlPluginRegistry* registry) {
|
||||
g_autoptr(FlPluginRegistrar) flowy_infra_ui_registrar =
|
||||
fl_plugin_registry_get_registrar_for_plugin(registry, "FlowyInfraUIPlugin");
|
||||
flowy_infra_u_i_plugin_register_with_registrar(flowy_infra_ui_registrar);
|
||||
g_autoptr(FlPluginRegistrar) rich_clipboard_linux_registrar =
|
||||
fl_plugin_registry_get_registrar_for_plugin(registry, "RichClipboardPlugin");
|
||||
rich_clipboard_plugin_register_with_registrar(rich_clipboard_linux_registrar);
|
||||
|
|
|
|||
|
|
@ -3,6 +3,7 @@
|
|||
#
|
||||
|
||||
list(APPEND FLUTTER_PLUGIN_LIST
|
||||
flowy_infra_ui
|
||||
rich_clipboard_linux
|
||||
url_launcher_linux
|
||||
)
|
||||
|
|
|
|||
|
|
@ -5,12 +5,16 @@
|
|||
import FlutterMacOS
|
||||
import Foundation
|
||||
|
||||
import flowy_infra_ui
|
||||
import path_provider_macos
|
||||
import rich_clipboard_macos
|
||||
import shared_preferences_macos
|
||||
import url_launcher_macos
|
||||
|
||||
func RegisterGeneratedPlugins(registry: FlutterPluginRegistry) {
|
||||
FlowyInfraUIPlugin.register(with: registry.registrar(forPlugin: "FlowyInfraUIPlugin"))
|
||||
PathProviderPlugin.register(with: registry.registrar(forPlugin: "PathProviderPlugin"))
|
||||
RichClipboardPlugin.register(with: registry.registrar(forPlugin: "RichClipboardPlugin"))
|
||||
SharedPreferencesPlugin.register(with: registry.registrar(forPlugin: "SharedPreferencesPlugin"))
|
||||
UrlLauncherPlugin.register(with: registry.registrar(forPlugin: "UrlLauncherPlugin"))
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,32 +1,44 @@
|
|||
PODS:
|
||||
- flowy_infra_ui (0.0.1):
|
||||
- FlutterMacOS
|
||||
- FlutterMacOS (1.0.0)
|
||||
- path_provider_macos (0.0.1):
|
||||
- FlutterMacOS
|
||||
- rich_clipboard_macos (0.0.1):
|
||||
- FlutterMacOS
|
||||
- shared_preferences_macos (0.0.1):
|
||||
- FlutterMacOS
|
||||
- url_launcher_macos (0.0.1):
|
||||
- FlutterMacOS
|
||||
|
||||
DEPENDENCIES:
|
||||
- flowy_infra_ui (from `Flutter/ephemeral/.symlinks/plugins/flowy_infra_ui/macos`)
|
||||
- FlutterMacOS (from `Flutter/ephemeral`)
|
||||
- path_provider_macos (from `Flutter/ephemeral/.symlinks/plugins/path_provider_macos/macos`)
|
||||
- rich_clipboard_macos (from `Flutter/ephemeral/.symlinks/plugins/rich_clipboard_macos/macos`)
|
||||
- shared_preferences_macos (from `Flutter/ephemeral/.symlinks/plugins/shared_preferences_macos/macos`)
|
||||
- url_launcher_macos (from `Flutter/ephemeral/.symlinks/plugins/url_launcher_macos/macos`)
|
||||
|
||||
EXTERNAL SOURCES:
|
||||
flowy_infra_ui:
|
||||
:path: Flutter/ephemeral/.symlinks/plugins/flowy_infra_ui/macos
|
||||
FlutterMacOS:
|
||||
:path: Flutter/ephemeral
|
||||
path_provider_macos:
|
||||
:path: Flutter/ephemeral/.symlinks/plugins/path_provider_macos/macos
|
||||
rich_clipboard_macos:
|
||||
:path: Flutter/ephemeral/.symlinks/plugins/rich_clipboard_macos/macos
|
||||
shared_preferences_macos:
|
||||
:path: Flutter/ephemeral/.symlinks/plugins/shared_preferences_macos/macos
|
||||
url_launcher_macos:
|
||||
:path: Flutter/ephemeral/.symlinks/plugins/url_launcher_macos/macos
|
||||
|
||||
SPEC CHECKSUMS:
|
||||
flowy_infra_ui: c34d49d615ed9fe552cd47f90d7850815a74e9e9
|
||||
FlutterMacOS: 57701585bf7de1b3fc2bb61f6378d73bbdea8424
|
||||
path_provider_macos: 3c0c3b4b0d4a76d2bf989a913c2de869c5641a19
|
||||
rich_clipboard_macos: 43364b66b9dc69d203eb8dd6d758e2d12e02723c
|
||||
shared_preferences_macos: a64dc611287ed6cbe28fd1297898db1336975727
|
||||
url_launcher_macos: 597e05b8e514239626bcf4a850fcf9ef5c856ec3
|
||||
|
||||
PODFILE CHECKSUM: 6eac6b3292e5142cfc23bdeb71848a40ec51c14c
|
||||
|
|
|
|||
|
|
@ -6,9 +6,12 @@
|
|||
|
||||
#include "generated_plugin_registrant.h"
|
||||
|
||||
#include <flowy_infra_ui/flowy_infra_u_i_plugin.h>
|
||||
#include <url_launcher_windows/url_launcher_windows.h>
|
||||
|
||||
void RegisterPlugins(flutter::PluginRegistry* registry) {
|
||||
FlowyInfraUIPluginRegisterWithRegistrar(
|
||||
registry->GetRegistrarForPlugin("FlowyInfraUIPlugin"));
|
||||
UrlLauncherWindowsRegisterWithRegistrar(
|
||||
registry->GetRegistrarForPlugin("UrlLauncherWindows"));
|
||||
}
|
||||
|
|
|
|||
|
|
@ -3,6 +3,7 @@
|
|||
#
|
||||
|
||||
list(APPEND FLUTTER_PLUGIN_LIST
|
||||
flowy_infra_ui
|
||||
url_launcher_windows
|
||||
)
|
||||
|
||||
|
|
|
|||
|
|
@ -15,6 +15,7 @@ export 'src/editor_state.dart';
|
|||
export 'src/core/transform/operation.dart';
|
||||
export 'src/core/transform/transaction.dart';
|
||||
export 'src/render/selection/selectable.dart';
|
||||
export 'src/render/selection_menu/selection_menu_service.dart';
|
||||
export 'src/service/editor_service.dart';
|
||||
export 'src/service/render_plugin_service.dart';
|
||||
export 'src/service/service.dart';
|
||||
|
|
|
|||
|
|
@ -131,16 +131,6 @@ class AppFlowyEditorLocalizations {
|
|||
);
|
||||
}
|
||||
|
||||
/// `Emoji`
|
||||
String get emoji {
|
||||
return Intl.message(
|
||||
'Emoji',
|
||||
name: 'emoji',
|
||||
desc: '',
|
||||
args: [],
|
||||
);
|
||||
}
|
||||
|
||||
/// `Image`
|
||||
String get image {
|
||||
return Intl.message(
|
||||
|
|
|
|||
|
|
@ -1,187 +0,0 @@
|
|||
import 'package:appflowy_editor/src/core/document/node.dart';
|
||||
import 'package:appflowy_editor/src/editor_state.dart';
|
||||
import 'package:appflowy_editor/src/infra/flowy_svg.dart';
|
||||
import 'package:appflowy_editor/src/render/selection_menu/selection_menu_service.dart';
|
||||
import 'package:appflowy_editor/src/render/style/editor_style.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:app_flowy/workspace/presentation/widgets/emoji_picker/src/emoji_picker.dart';
|
||||
import 'package:appflowy_editor/appflowy_editor.dart';
|
||||
|
||||
import 'package:app_flowy/workspace/presentation/widgets/emoji_picker/src/models/emoji_model.dart';
|
||||
import 'package:app_flowy/workspace/presentation/widgets/emoji_picker/src/config.dart';
|
||||
import 'package:app_flowy/workspace/presentation/widgets/emoji_picker/src/emoji_button.dart';
|
||||
import 'dart:collection';
|
||||
|
||||
import 'package:appflowy_popover/appflowy_popover.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter/services.dart';
|
||||
import 'package:flutter/foundation.dart' as foundation;
|
||||
|
||||
OverlayEntry? _imojiSelectionMenu;
|
||||
EditorState? _editorState;
|
||||
void showEmojiSelectionMenu(
|
||||
EditorState editorState,
|
||||
SelectionMenuService menuService,
|
||||
BuildContext context,
|
||||
) {
|
||||
menuService.dismiss();
|
||||
|
||||
_imojiSelectionMenu?.remove();
|
||||
_imojiSelectionMenu = OverlayEntry(builder: (context) {
|
||||
return Positioned(
|
||||
top: menuService.topLeft.dy,
|
||||
left: menuService.topLeft.dx,
|
||||
child: Material(
|
||||
child: EmojiSelectionMenu(
|
||||
editorState: editorState,
|
||||
onSubmitted: (text) {
|
||||
// insert emoji
|
||||
editorState.insertEmojiNode(text);
|
||||
},
|
||||
onExit: () {
|
||||
_dismissEmojiSelectionMenu();
|
||||
//close emoji panel
|
||||
},
|
||||
),
|
||||
),
|
||||
);
|
||||
});
|
||||
|
||||
Overlay.of(context)?.insert(_imojiSelectionMenu!);
|
||||
|
||||
editorState.service.selectionService.currentSelection
|
||||
.addListener(_dismissEmojiSelectionMenu);
|
||||
}
|
||||
|
||||
void _dismissEmojiSelectionMenu() {
|
||||
_imojiSelectionMenu?.remove();
|
||||
_imojiSelectionMenu = null;
|
||||
|
||||
_editorState?.service.selectionService.currentSelection
|
||||
.removeListener(_dismissEmojiSelectionMenu);
|
||||
_editorState = null;
|
||||
}
|
||||
|
||||
class EmojiSelectionMenu extends StatefulWidget {
|
||||
const EmojiSelectionMenu({
|
||||
Key? key,
|
||||
required this.onSubmitted,
|
||||
required this.onExit,
|
||||
this.editorState,
|
||||
}) : super(key: key);
|
||||
|
||||
final void Function(Emoji Emoji) onSubmitted;
|
||||
final void Function() onExit;
|
||||
final EditorState? editorState;
|
||||
|
||||
@override
|
||||
State<EmojiSelectionMenu> createState() => _EmojiSelectionMenuState();
|
||||
}
|
||||
|
||||
class _EmojiSelectionMenuState extends State<EmojiSelectionMenu> {
|
||||
EditorStyle? get style => widget.editorState?.editorStyle;
|
||||
|
||||
@override
|
||||
void initState() {
|
||||
HardwareKeyboard.instance.addHandler(_handleGlobalKeyEvent);
|
||||
super.initState();
|
||||
}
|
||||
|
||||
bool _handleGlobalKeyEvent(KeyEvent event) {
|
||||
if (event.logicalKey == LogicalKeyboardKey.escape &&
|
||||
event is KeyDownEvent) {
|
||||
//triggers on esc
|
||||
widget.onExit();
|
||||
return true;
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
@override
|
||||
void deactivate() {
|
||||
HardwareKeyboard.instance.removeHandler(_handleGlobalKeyEvent);
|
||||
super.deactivate();
|
||||
}
|
||||
|
||||
@override
|
||||
void dispose() {
|
||||
super.dispose();
|
||||
}
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return Container(
|
||||
width: 300,
|
||||
padding: const EdgeInsets.all(24.0),
|
||||
decoration: BoxDecoration(
|
||||
color: style?.selectionMenuBackgroundColor ?? Colors.white,
|
||||
boxShadow: [
|
||||
BoxShadow(
|
||||
blurRadius: 5,
|
||||
spreadRadius: 1,
|
||||
color: Colors.black.withOpacity(0.1),
|
||||
),
|
||||
],
|
||||
// borderRadius: BorderRadius.circular(6.0),
|
||||
),
|
||||
child: Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
_buildHeader(context),
|
||||
FlowyEmojiStyleButton(normalIcon: '', tooltipText: ''),
|
||||
const SizedBox(height: 10.0),
|
||||
_buildEmojiBox(context),
|
||||
],
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
Widget _buildHeader(BuildContext context) {
|
||||
return Text(
|
||||
'Pick Emoji',
|
||||
textAlign: TextAlign.left,
|
||||
style: TextStyle(
|
||||
fontSize: 14.0,
|
||||
color: style?.selectionMenuItemTextColor ?? Colors.black,
|
||||
fontWeight: FontWeight.w500,
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
Widget _buildEmojiBox(BuildContext context) {
|
||||
return SizedBox(
|
||||
height: 300,
|
||||
child: EmojiPicker(
|
||||
onEmojiSelected: (category, emoji) => widget.onSubmitted(emoji),
|
||||
config: const Config(
|
||||
columns: 8,
|
||||
emojiSizeMax: 28,
|
||||
bgColor: Color(0xffF2F2F2),
|
||||
iconColor: Colors.grey,
|
||||
iconColorSelected: Color(0xff333333),
|
||||
indicatorColor: Color(0xff333333),
|
||||
progressIndicatorColor: Color(0xff333333),
|
||||
buttonMode: ButtonMode.CUPERTINO,
|
||||
initCategory: Category.RECENT,
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
extension on EditorState {
|
||||
void insertEmojiNode(Emoji emoji) {
|
||||
final selectionService = service.selectionService;
|
||||
final currentSelection = selectionService.currentSelection.value;
|
||||
|
||||
if (currentSelection == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
final textNode = selectionService.currentSelectedNodes.first as TextNode;
|
||||
final transaction = this.transaction;
|
||||
transaction.insertText(textNode, currentSelection.endIndex, emoji.emoji);
|
||||
apply(transaction);
|
||||
}
|
||||
}
|
||||
|
|
@ -1,15 +1,17 @@
|
|||
import 'package:appflowy_editor/src/editor_state.dart';
|
||||
import 'package:appflowy_editor/src/infra/flowy_svg.dart';
|
||||
import 'package:appflowy_editor/src/l10n/l10n.dart';
|
||||
import 'package:appflowy_editor/src/render/image/image_upload_widget.dart';
|
||||
import 'package:appflowy_editor/src/render/selection_menu/selection_menu_widget.dart';
|
||||
import 'package:appflowy_editor/src/service/default_text_operations/format_rich_text_style.dart';
|
||||
import 'package:appflowy_editor/src/render/emoji/emoji_select_widget.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:appflowy_editor/src/core/legacy/built_in_attribute_keys.dart';
|
||||
|
||||
import '../../core/legacy/built_in_attribute_keys.dart';
|
||||
import '../../editor_state.dart';
|
||||
import '../../infra/flowy_svg.dart';
|
||||
import '../../l10n/l10n.dart';
|
||||
import '../../service/default_text_operations/format_rich_text_style.dart';
|
||||
import '../image/image_upload_widget.dart';
|
||||
import 'selection_menu_widget.dart';
|
||||
|
||||
abstract class SelectionMenuService {
|
||||
Offset get topLeft;
|
||||
Offset get offset;
|
||||
Alignment get alignment;
|
||||
|
||||
void show();
|
||||
void dismiss();
|
||||
|
|
@ -27,6 +29,8 @@ class SelectionMenu implements SelectionMenuService {
|
|||
OverlayEntry? _selectionMenuEntry;
|
||||
bool _selectionUpdateByInner = false;
|
||||
Offset? _topLeft;
|
||||
Offset _offset = Offset.zero;
|
||||
Alignment _alignment = Alignment.topLeft;
|
||||
|
||||
@override
|
||||
void dismiss() {
|
||||
|
|
@ -67,6 +71,7 @@ class SelectionMenu implements SelectionMenuService {
|
|||
|
||||
// show below defualt
|
||||
var showBelow = true;
|
||||
_alignment = Alignment.bottomLeft;
|
||||
final bottomRight = selectionRects.first.bottomRight;
|
||||
final topRight = selectionRects.first.topRight;
|
||||
var offset = bottomRight + menuOffset;
|
||||
|
|
@ -75,14 +80,16 @@ class SelectionMenu implements SelectionMenuService {
|
|||
// show above
|
||||
offset = topRight - menuOffset;
|
||||
showBelow = false;
|
||||
_alignment = Alignment.topRight;
|
||||
}
|
||||
_topLeft = offset;
|
||||
_offset = Offset(offset.dx, showBelow ? offset.dy : MediaQuery.of(context).size.height - offset.dy);
|
||||
|
||||
_selectionMenuEntry = OverlayEntry(builder: (context) {
|
||||
return Positioned(
|
||||
top: showBelow ? offset.dy : null,
|
||||
top: showBelow ? _offset.dy : null,
|
||||
bottom:
|
||||
showBelow ? null : MediaQuery.of(context).size.height - offset.dy,
|
||||
showBelow ? null : _offset.dy,
|
||||
left: offset.dx,
|
||||
child: SelectionMenuWidget(
|
||||
items: [
|
||||
|
|
@ -114,6 +121,16 @@ class SelectionMenu implements SelectionMenuService {
|
|||
return _topLeft ?? Offset.zero;
|
||||
}
|
||||
|
||||
@override
|
||||
Alignment get alignment {
|
||||
return _alignment;
|
||||
}
|
||||
|
||||
@override
|
||||
Offset get offset {
|
||||
return _offset;
|
||||
}
|
||||
|
||||
void _onSelectionChange() {
|
||||
// workaround: SelectionService has been released after hot reload.
|
||||
final isSelectionDisposed =
|
||||
|
|
@ -181,13 +198,6 @@ final List<SelectionMenuItem> _defaultSelectionMenuItems = [
|
|||
keywords: ['image'],
|
||||
handler: showImageUploadMenu,
|
||||
),
|
||||
SelectionMenuItem(
|
||||
name: () => AppFlowyEditorLocalizations.current.emoji,
|
||||
icon: (editorState, onSelected) =>
|
||||
_selectionMenuIcon('image', editorState, onSelected),
|
||||
keywords: ['emoji'],
|
||||
handler: showEmojiSelectionMenu,
|
||||
),
|
||||
SelectionMenuItem(
|
||||
name: () => AppFlowyEditorLocalizations.current.bulletedList,
|
||||
icon: (editorState, onSelected) =>
|
||||
|
|
|
|||
Loading…
Reference in a new issue