mirror of
https://github.com/twonlyapp/twonly-app.git
synced 2026-03-03 17:06:47 +00:00
improve image editor
This commit is contained in:
parent
cd5deca6b6
commit
2ef4566d69
44 changed files with 465 additions and 1040 deletions
|
|
@ -5,6 +5,8 @@
|
|||
- Adds option to manual focus in the camera
|
||||
- Adds support to switch between front and back camera during video recording
|
||||
- Adds basic face filters
|
||||
- Improves image editor like emojies or text under a drawing can be moved
|
||||
- Fixes issue with emojis disappearing in the image editor
|
||||
|
||||
## 0.0.86
|
||||
|
||||
|
|
|
|||
|
|
@ -22,7 +22,7 @@ class ReactionsDao extends DatabaseAccessor<TwonlyDB> with _$ReactionsDaoMixin {
|
|||
String emoji,
|
||||
bool remove,
|
||||
) async {
|
||||
if (!isEmoji(emoji)) {
|
||||
if (!isOneEmoji(emoji)) {
|
||||
Log.error('Did not update reaction as it is not an emoji!');
|
||||
return;
|
||||
}
|
||||
|
|
@ -59,7 +59,7 @@ class ReactionsDao extends DatabaseAccessor<TwonlyDB> with _$ReactionsDaoMixin {
|
|||
String emoji,
|
||||
bool remove,
|
||||
) async {
|
||||
if (!isEmoji(emoji)) {
|
||||
if (!isOneEmoji(emoji)) {
|
||||
Log.error('Did not update reaction as it is not an emoji!');
|
||||
return;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -13,7 +13,7 @@ import 'package:twonly/src/services/api/mediafiles/upload.service.dart';
|
|||
import 'package:twonly/src/services/signal/session.signal.dart';
|
||||
import 'package:twonly/src/utils/log.dart';
|
||||
import 'package:twonly/src/utils/misc.dart';
|
||||
import 'package:twonly/src/views/camera/share_image_editor_view.dart';
|
||||
import 'package:twonly/src/views/camera/share_image_editor.view.dart';
|
||||
import 'package:twonly/src/views/chats/add_new_user.view.dart';
|
||||
import 'package:twonly/src/views/components/alert_dialog.dart';
|
||||
import 'package:twonly/src/views/contact/contact.view.dart';
|
||||
|
|
|
|||
|
|
@ -23,7 +23,9 @@ class MainCameraPreview extends StatelessWidget {
|
|||
requiredHeight: 0,
|
||||
additionalPadding: 59,
|
||||
bottomNavigation: Container(),
|
||||
child: Screenshot(
|
||||
child: Stack(
|
||||
children: [
|
||||
Screenshot(
|
||||
controller: mainCameraController.screenshotController,
|
||||
child: AspectRatio(
|
||||
aspectRatio: 9 / 16,
|
||||
|
|
@ -48,7 +50,27 @@ class MainCameraPreview extends StatelessWidget {
|
|||
Positioned.fill(
|
||||
child: mainCameraController.facePaint!,
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
if (mainCameraController.focusPointOffset != null)
|
||||
AspectRatio(
|
||||
aspectRatio: 9 / 16,
|
||||
child: ClipRect(
|
||||
child: FittedBox(
|
||||
fit: BoxFit.cover,
|
||||
child: SizedBox(
|
||||
width: mainCameraController
|
||||
.cameraController!.value.previewSize!.height,
|
||||
height: mainCameraController
|
||||
.cameraController!.value.previewSize!.width,
|
||||
child: Stack(
|
||||
children: [
|
||||
Positioned(
|
||||
top: mainCameraController.focusPointOffset!.dy - 40,
|
||||
left:
|
||||
|
|
@ -65,14 +87,14 @@ class MainCameraPreview extends StatelessWidget {
|
|||
),
|
||||
),
|
||||
),
|
||||
)
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
);
|
||||
|
|
|
|||
|
|
@ -27,8 +27,8 @@ import 'package:twonly/src/views/camera/camera_preview_components/permissions_vi
|
|||
import 'package:twonly/src/views/camera/camera_preview_components/send_to.dart';
|
||||
import 'package:twonly/src/views/camera/camera_preview_components/video_recording_time.dart';
|
||||
import 'package:twonly/src/views/camera/camera_preview_components/zoom_selector.dart';
|
||||
import 'package:twonly/src/views/camera/image_editor/action_button.dart';
|
||||
import 'package:twonly/src/views/camera/share_image_editor_view.dart';
|
||||
import 'package:twonly/src/views/camera/share_image_editor/action_button.dart';
|
||||
import 'package:twonly/src/views/camera/share_image_editor.view.dart';
|
||||
import 'package:twonly/src/views/components/avatar_icon.component.dart';
|
||||
import 'package:twonly/src/views/components/loader.dart';
|
||||
import 'package:twonly/src/views/components/media_view_sizing.dart';
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
import 'package:flutter/material.dart';
|
||||
import 'package:twonly/src/views/camera/painters/face_filters/beard_filter_painter.dart';
|
||||
import 'package:twonly/src/views/camera/painters/face_filters/dog_filter_painter.dart';
|
||||
import 'package:twonly/src/views/camera/camera_preview_components/painters/face_filters/beard_filter_painter.dart';
|
||||
import 'package:twonly/src/views/camera/camera_preview_components/painters/face_filters/dog_filter_painter.dart';
|
||||
|
||||
enum FaceFilterType {
|
||||
none,
|
||||
|
|
|
|||
|
|
@ -19,10 +19,10 @@ import 'package:twonly/src/utils/qr.dart';
|
|||
import 'package:twonly/src/utils/screenshot.dart';
|
||||
import 'package:twonly/src/views/camera/camera_preview_components/camera_preview_controller_view.dart';
|
||||
import 'package:twonly/src/views/camera/camera_preview_components/face_filters.dart';
|
||||
import 'package:twonly/src/views/camera/painters/barcode_detector_painter.dart';
|
||||
import 'package:twonly/src/views/camera/painters/face_filters/beard_filter_painter.dart';
|
||||
import 'package:twonly/src/views/camera/painters/face_filters/dog_filter_painter.dart';
|
||||
import 'package:twonly/src/views/camera/painters/face_filters/face_filter_painter.dart';
|
||||
import 'package:twonly/src/views/camera/camera_preview_components/painters/barcode_detector_painter.dart';
|
||||
import 'package:twonly/src/views/camera/camera_preview_components/painters/face_filters/beard_filter_painter.dart';
|
||||
import 'package:twonly/src/views/camera/camera_preview_components/painters/face_filters/dog_filter_painter.dart';
|
||||
import 'package:twonly/src/views/camera/camera_preview_components/painters/face_filters/face_filter_painter.dart';
|
||||
|
||||
class ScannedVerifiedContact {
|
||||
ScannedVerifiedContact({
|
||||
|
|
|
|||
|
|
@ -6,8 +6,8 @@ import 'package:flutter/material.dart';
|
|||
import 'package:flutter/services.dart';
|
||||
import 'package:google_mlkit_face_detection/google_mlkit_face_detection.dart';
|
||||
import 'package:twonly/src/utils/log.dart';
|
||||
import 'package:twonly/src/views/camera/painters/coordinates_translator.dart';
|
||||
import 'package:twonly/src/views/camera/painters/face_filters/face_filter_painter.dart';
|
||||
import 'package:twonly/src/views/camera/camera_preview_components/painters/coordinates_translator.dart';
|
||||
import 'package:twonly/src/views/camera/camera_preview_components/painters/face_filters/face_filter_painter.dart';
|
||||
|
||||
class BeardFilterPainter extends FaceFilterPainter {
|
||||
BeardFilterPainter(
|
||||
|
|
@ -6,8 +6,8 @@ import 'package:flutter/material.dart';
|
|||
import 'package:flutter/services.dart';
|
||||
import 'package:google_mlkit_face_detection/google_mlkit_face_detection.dart';
|
||||
import 'package:twonly/src/utils/log.dart';
|
||||
import 'package:twonly/src/views/camera/painters/coordinates_translator.dart';
|
||||
import 'package:twonly/src/views/camera/painters/face_filters/face_filter_painter.dart';
|
||||
import 'package:twonly/src/views/camera/camera_preview_components/painters/coordinates_translator.dart';
|
||||
import 'package:twonly/src/views/camera/camera_preview_components/painters/face_filters/face_filter_painter.dart';
|
||||
|
||||
class DogFilterPainter extends FaceFilterPainter {
|
||||
DogFilterPainter(
|
||||
|
|
@ -1,710 +0,0 @@
|
|||
Map<String, String> emojiWeights = {};
|
||||
|
||||
List<String> emojis = [
|
||||
'😀',
|
||||
'😁',
|
||||
'😂',
|
||||
'🤣',
|
||||
'😃',
|
||||
'😄',
|
||||
'😅',
|
||||
'😆',
|
||||
'😉',
|
||||
'😊',
|
||||
'😋',
|
||||
'😎',
|
||||
'😍',
|
||||
'😘',
|
||||
'🥰',
|
||||
'😗',
|
||||
'😙',
|
||||
'😚',
|
||||
'🙂️',
|
||||
'🤗',
|
||||
'🤩',
|
||||
'🤔',
|
||||
'🤔',
|
||||
'🤨',
|
||||
'😐',
|
||||
'😑',
|
||||
'😶',
|
||||
'🙄',
|
||||
'😏',
|
||||
'😣',
|
||||
'😥',
|
||||
'😮',
|
||||
'🤐',
|
||||
'😯',
|
||||
'😪',
|
||||
'😫',
|
||||
'😴',
|
||||
'😌',
|
||||
'😛',
|
||||
'😜',
|
||||
'😝',
|
||||
'🤤',
|
||||
'😒',
|
||||
'😓',
|
||||
'😔',
|
||||
'😕',
|
||||
'🙃',
|
||||
'🤑',
|
||||
'😲',
|
||||
'🙁',
|
||||
'😖',
|
||||
'😞',
|
||||
'😟',
|
||||
'😤',
|
||||
'😢',
|
||||
'😭',
|
||||
'😦',
|
||||
'😧',
|
||||
'😨',
|
||||
'😩',
|
||||
'🤯',
|
||||
'😬',
|
||||
'😰',
|
||||
'😱',
|
||||
'🥵',
|
||||
'🥶',
|
||||
'😳',
|
||||
'🤪',
|
||||
'😵',
|
||||
'😡',
|
||||
'😠',
|
||||
'🤬',
|
||||
'😷',
|
||||
'🤒',
|
||||
'🤕',
|
||||
'🤢',
|
||||
'🤮',
|
||||
'🤧',
|
||||
'😇',
|
||||
'🤠',
|
||||
'🤡',
|
||||
'🥳',
|
||||
'🥴',
|
||||
'🤥',
|
||||
'🤫',
|
||||
'🤭',
|
||||
'🤭',
|
||||
'🧐',
|
||||
'🤓',
|
||||
'😈',
|
||||
'👿',
|
||||
'👹',
|
||||
'👺',
|
||||
'💀',
|
||||
'👻',
|
||||
'👽',
|
||||
'🤖',
|
||||
'💩',
|
||||
'😺',
|
||||
'😸',
|
||||
'😹',
|
||||
'😻',
|
||||
'😼',
|
||||
'😽',
|
||||
'🙀',
|
||||
'😿',
|
||||
'😾',
|
||||
'😾',
|
||||
|
||||
/// People and Fantasy
|
||||
'👶',
|
||||
'👧',
|
||||
'🧒',
|
||||
'👩',
|
||||
'🧑',
|
||||
'👨',
|
||||
'👵',
|
||||
'👴',
|
||||
'👲',
|
||||
'👳♀️️',
|
||||
'👳♂️️️',
|
||||
'🧕️️️',
|
||||
'🧔',
|
||||
'👱♂️️',
|
||||
'👱♀️️️',
|
||||
'👨🦰️️️',
|
||||
'👩🦰',
|
||||
'👨🦱',
|
||||
'👨🦲',
|
||||
'👩🦲',
|
||||
'👨🦳',
|
||||
'👩🦳',
|
||||
'🦸♀️',
|
||||
'🦸♂️️',
|
||||
'🦹♀️️️',
|
||||
'🦹♂️️️️',
|
||||
'👮♀️',
|
||||
'👮♂️️',
|
||||
'👷♀️️️',
|
||||
'👷♂️️️️',
|
||||
'💂♀️️️️️',
|
||||
'💂♂️️️️️️',
|
||||
'🕵️♀️️️️️️️',
|
||||
'🕵️♂️️️️️️️️',
|
||||
'👩⚕️️️️️️️️️',
|
||||
'👨⚕️️️️️️️️️️',
|
||||
'👩🌾️️️️️️️️️️',
|
||||
'👨🌾',
|
||||
'👩🍳',
|
||||
'👨🍳',
|
||||
'👩🎓',
|
||||
'👨🎓',
|
||||
'👩🎤',
|
||||
'👨🎤',
|
||||
'👩🏫',
|
||||
'👨🏫',
|
||||
'👩🏭',
|
||||
'👨🏭',
|
||||
'👩💻',
|
||||
'👨💻',
|
||||
'👩💼',
|
||||
'👨💼',
|
||||
'👩🔧',
|
||||
'👨🔧',
|
||||
'👩🔬',
|
||||
'👨🔬',
|
||||
'👩🎨',
|
||||
'👨🎨',
|
||||
'👩🚒',
|
||||
'👨🚒',
|
||||
'👩✈️',
|
||||
'👨✈️️',
|
||||
'👩🚀',
|
||||
'👨🚀',
|
||||
'👩⚖️',
|
||||
'👨⚖️️',
|
||||
'👰',
|
||||
'🤵',
|
||||
'👸',
|
||||
'🤴',
|
||||
'🤶',
|
||||
'🎅',
|
||||
'🧙♀️',
|
||||
'🧙♂️️',
|
||||
'🧝♀️️️',
|
||||
'🧝♂️',
|
||||
'🧛♀️️',
|
||||
'🧛♂️️️',
|
||||
'🧟♀️️️️',
|
||||
'🧟♂️️️️️',
|
||||
'🧞♀️️️️️️',
|
||||
'🧞♂️️️️️️️',
|
||||
'🧜♀️️️️️️️️',
|
||||
'🧜♂️️️️️️️️️',
|
||||
'🧚♀️️️️️️️️️️',
|
||||
'🧚♂️️️️️️️️️️️',
|
||||
'👼️️️️️️️️️️️',
|
||||
'🤰',
|
||||
'🤱',
|
||||
'🙇♀️',
|
||||
'🙇♂️',
|
||||
'💁♀️️',
|
||||
'💁♂️️️',
|
||||
'🙅♀️️️️',
|
||||
'🙅♂️',
|
||||
'🙆♀️️',
|
||||
'🙆♂️️️',
|
||||
'🙋♀️️️️',
|
||||
'🙋♂️',
|
||||
'🤦♀️️',
|
||||
'🤦♂️️️',
|
||||
'🤷♀️️️️',
|
||||
'🤷♂️️️️️',
|
||||
'🙎♀️️️️️️',
|
||||
'🙎♂️️️️️️️',
|
||||
'🙍♀️️️️️️️️',
|
||||
'🙍♂️️️️️️️️️',
|
||||
'💇♀️️️️️️️️️️',
|
||||
'💇♂️️️️️️️️️️️',
|
||||
'💆♀️️️️️️️️️️️️',
|
||||
'💆♂️️️️️️️️️️️️️',
|
||||
'🧖♀️️️️️️️️️️️️️️',
|
||||
'🧖♂️️️️️️️️️️️️️️️',
|
||||
'💅️️️️️️️️️️️️️️️',
|
||||
'🤳️️️️️️️️️️️️️️',
|
||||
'💃️️️️️️️️️️️️️',
|
||||
'🕺️️️️️️️️️️️️',
|
||||
'👯♀️',
|
||||
'👯♂️️',
|
||||
'🕴️️',
|
||||
'🚶♀️️',
|
||||
'🚶♂️️️',
|
||||
'🏃♀️️️️',
|
||||
'🏃♂️',
|
||||
'👫️',
|
||||
'👭',
|
||||
'👬',
|
||||
'💑',
|
||||
'👩❤️👩',
|
||||
'👨❤️👨',
|
||||
'💏',
|
||||
'👩❤️💋👩',
|
||||
'👨❤️💋👨',
|
||||
'👪',
|
||||
'👨👩👧',
|
||||
'👨👩👧👦',
|
||||
'👨👩👦👦',
|
||||
'👨👩👧👧',
|
||||
'👩👩👦',
|
||||
'👩👩👧',
|
||||
'👩👩👧👦',
|
||||
'👩👩👦👦',
|
||||
'👩👩👧👧',
|
||||
'👨👨👦',
|
||||
'👨👨👧',
|
||||
'👨👨👧👦',
|
||||
'👨👨👦👦',
|
||||
'👨👨👧👧',
|
||||
'👩👦',
|
||||
'👩👧',
|
||||
'👩👧👦',
|
||||
'👩👦👦',
|
||||
'👩👧👧',
|
||||
'👨👦',
|
||||
'👨👧',
|
||||
'👨👧👦',
|
||||
'👨👦👦',
|
||||
'👨👧👧',
|
||||
'🤲',
|
||||
'👐',
|
||||
'🙌',
|
||||
'👏',
|
||||
'🤝',
|
||||
'👍',
|
||||
'👎',
|
||||
'👊',
|
||||
'✊',
|
||||
'🤛',
|
||||
'🤜',
|
||||
'🤞',
|
||||
'✌️',
|
||||
'🤟️',
|
||||
'🤘',
|
||||
'👌',
|
||||
'👈',
|
||||
'👉',
|
||||
'👆',
|
||||
'👇',
|
||||
'☝️',
|
||||
'✋️',
|
||||
'🤚️',
|
||||
'🤚️',
|
||||
'🖐',
|
||||
'🖖',
|
||||
'👋',
|
||||
'🤙',
|
||||
'💪',
|
||||
'🦵',
|
||||
'🦶',
|
||||
'🖕',
|
||||
'✍️',
|
||||
'🙏️',
|
||||
'💍',
|
||||
'💄',
|
||||
'💋',
|
||||
'👄',
|
||||
'👅',
|
||||
'👂',
|
||||
'👃',
|
||||
'👣',
|
||||
'👁',
|
||||
'👀',
|
||||
'🧠',
|
||||
'🦴',
|
||||
'🦷',
|
||||
'🗣',
|
||||
'👤',
|
||||
'👥',
|
||||
'🧥',
|
||||
'👚',
|
||||
'👕',
|
||||
'👖',
|
||||
'👔',
|
||||
'👗',
|
||||
'👙',
|
||||
'👘',
|
||||
'👠',
|
||||
'👡',
|
||||
'👢',
|
||||
'👞',
|
||||
'👟',
|
||||
'🥾',
|
||||
'🥿',
|
||||
'🧦',
|
||||
'🧤',
|
||||
'🧣',
|
||||
'🎩',
|
||||
'🧢',
|
||||
'👒',
|
||||
'🎓',
|
||||
'⛑',
|
||||
'👑',
|
||||
'👝',
|
||||
'👛',
|
||||
'👜',
|
||||
'💼',
|
||||
'🎒',
|
||||
'👓',
|
||||
'🕶',
|
||||
'🥽',
|
||||
'🥼',
|
||||
'🌂',
|
||||
'🧵',
|
||||
'🧶',
|
||||
|
||||
/// Animals
|
||||
'🐶',
|
||||
'🐱',
|
||||
'🐭',
|
||||
'🐰',
|
||||
'🦊',
|
||||
'🦝',
|
||||
'🐻',
|
||||
'🦘',
|
||||
'🦡',
|
||||
'🐨',
|
||||
'🐯',
|
||||
'🦁',
|
||||
'🐼',
|
||||
'🐼',
|
||||
'🐮',
|
||||
'🐷',
|
||||
'🐽',
|
||||
'🐸',
|
||||
'🐵',
|
||||
'🙈',
|
||||
'🙉',
|
||||
'🙊',
|
||||
'🐒',
|
||||
'🐔',
|
||||
'🐧',
|
||||
'🐦',
|
||||
'🐤',
|
||||
'🐣',
|
||||
'🐥',
|
||||
'🦆',
|
||||
'🦢',
|
||||
'🦅',
|
||||
'🦉',
|
||||
'🦚',
|
||||
'🦜',
|
||||
'🦇',
|
||||
'🐺',
|
||||
'🐗',
|
||||
'🐴',
|
||||
'🦄',
|
||||
'🐝',
|
||||
'🐛',
|
||||
'🦋',
|
||||
'🐌',
|
||||
'🐚',
|
||||
'🐞',
|
||||
'🐜',
|
||||
'🦗',
|
||||
'🕷',
|
||||
'🕸',
|
||||
'🦂',
|
||||
'🦟',
|
||||
'🦠',
|
||||
'🐢',
|
||||
'🐍',
|
||||
'🦎',
|
||||
'🦖',
|
||||
'🦕',
|
||||
'🐙',
|
||||
'🦑',
|
||||
'🦐',
|
||||
'🦀',
|
||||
'🐡',
|
||||
'🐠',
|
||||
'🐟',
|
||||
'🐬',
|
||||
'🐳',
|
||||
'🐋',
|
||||
'🦈',
|
||||
'🐊',
|
||||
'🐅',
|
||||
'🐆',
|
||||
'🦓',
|
||||
'🦍',
|
||||
'🐘',
|
||||
'🦏',
|
||||
'🦛',
|
||||
'🐪',
|
||||
'🐫',
|
||||
'🦙',
|
||||
'🦒',
|
||||
'🐃',
|
||||
'🐂',
|
||||
'🐄',
|
||||
'🐎',
|
||||
'🐖',
|
||||
'🐏',
|
||||
'🐐',
|
||||
'🦌',
|
||||
'🐕',
|
||||
'🐩',
|
||||
'🐈',
|
||||
'🐓',
|
||||
'🦃',
|
||||
'🕊',
|
||||
'🐇',
|
||||
'🐁',
|
||||
'🐀',
|
||||
'🐿',
|
||||
'🦔',
|
||||
'🐾',
|
||||
'🐉',
|
||||
'🐲',
|
||||
'🌵',
|
||||
'🎄',
|
||||
'🌲',
|
||||
'🌳',
|
||||
'🌴',
|
||||
'🌱',
|
||||
'🌿',
|
||||
'☘️',
|
||||
'🎍️',
|
||||
'🎋️',
|
||||
'🍃',
|
||||
'🍂',
|
||||
'🍁',
|
||||
'🍄',
|
||||
'🌾️',
|
||||
'💐️',
|
||||
'🌷️',
|
||||
'🌹',
|
||||
'🥀',
|
||||
'🌺',
|
||||
'🌸',
|
||||
'🌼',
|
||||
'🌻️',
|
||||
'🌞',
|
||||
'🌝',
|
||||
'🌛',
|
||||
'🌜',
|
||||
'🌚',
|
||||
'🌕',
|
||||
'🌖',
|
||||
'🌗',
|
||||
'🌘',
|
||||
'🌑',
|
||||
'🌒',
|
||||
'🌔',
|
||||
'🌙',
|
||||
'🌎',
|
||||
'🌍',
|
||||
'🌏',
|
||||
'💫',
|
||||
'⭐️',
|
||||
'🌟️',
|
||||
'✨️',
|
||||
'⚡️️',
|
||||
'☄️️️',
|
||||
'💥️️️',
|
||||
'🔥',
|
||||
'🌪',
|
||||
'🌈',
|
||||
'☀️',
|
||||
'🌤️',
|
||||
'⛅️️',
|
||||
'🌥️️',
|
||||
'☁️️',
|
||||
'🌦️️',
|
||||
'🌧️',
|
||||
'⛈',
|
||||
'🌩',
|
||||
'🌨',
|
||||
'❄️',
|
||||
'☃️️',
|
||||
'⛄️️️',
|
||||
'🌬️️️',
|
||||
'💨️️️',
|
||||
'💧️️️',
|
||||
'💦️️️',
|
||||
'☔️️️️',
|
||||
'☂️️️️️',
|
||||
'🌊️️️️️',
|
||||
'🌫️️️️',
|
||||
|
||||
/// Foods
|
||||
'🍏',
|
||||
'🍎',
|
||||
'🍐',
|
||||
'🍊',
|
||||
'🍋',
|
||||
'🍌',
|
||||
'🍉',
|
||||
'🍇',
|
||||
'🍓',
|
||||
'🍈',
|
||||
'🍒',
|
||||
'🍑',
|
||||
'🍍',
|
||||
'🥭',
|
||||
'🥥',
|
||||
'🥝',
|
||||
'🍅',
|
||||
'🍆',
|
||||
'🥑',
|
||||
'🥦',
|
||||
'🥒',
|
||||
'🥬',
|
||||
'🌶',
|
||||
'🌽',
|
||||
'🥕',
|
||||
'🥔',
|
||||
'🍠',
|
||||
'🥐',
|
||||
'🍞',
|
||||
'🥖',
|
||||
'🥨',
|
||||
'🥯',
|
||||
'🧀',
|
||||
'🥚',
|
||||
'🍳',
|
||||
'🥞',
|
||||
'🥓',
|
||||
'🥩',
|
||||
'🍗',
|
||||
'🍖',
|
||||
'🌭',
|
||||
'🍔',
|
||||
'🍟',
|
||||
'🍕',
|
||||
'🥪',
|
||||
'🥙',
|
||||
'🌮',
|
||||
'🌯',
|
||||
'🥗',
|
||||
'🥘',
|
||||
'🥫',
|
||||
'🍝',
|
||||
'🍜',
|
||||
'🍲',
|
||||
'🍛',
|
||||
'🍣',
|
||||
'🍱',
|
||||
'🥟',
|
||||
'🍤',
|
||||
'🍙',
|
||||
'🍚',
|
||||
'🍘',
|
||||
'🍥',
|
||||
'🥮',
|
||||
'🥠',
|
||||
'🍢',
|
||||
'🍧',
|
||||
'🍨',
|
||||
'🍦',
|
||||
'🥧',
|
||||
'🍰',
|
||||
'🎂',
|
||||
'🍮',
|
||||
'🍭',
|
||||
'🍬',
|
||||
'🍫',
|
||||
'🍿',
|
||||
'🧂',
|
||||
'🍩',
|
||||
'🍪',
|
||||
'🌰',
|
||||
'🥜',
|
||||
'🍯',
|
||||
'🥛',
|
||||
'🍼',
|
||||
'☕️',
|
||||
'🍵️',
|
||||
'🥤️',
|
||||
'🍶',
|
||||
'🍺',
|
||||
'🍻',
|
||||
'🥂',
|
||||
'🍷',
|
||||
'🍸',
|
||||
'🍹',
|
||||
'🍾',
|
||||
'🥄',
|
||||
'🍴',
|
||||
'🍽',
|
||||
'🥣',
|
||||
'🥡',
|
||||
'🥢',
|
||||
|
||||
/// Activity and Sports
|
||||
'⚽️',
|
||||
'🏀️',
|
||||
'🏈',
|
||||
'⚾️',
|
||||
'🥎️',
|
||||
'🏐️',
|
||||
'🏉',
|
||||
'🎾',
|
||||
'🥏',
|
||||
'🎱',
|
||||
'🏓',
|
||||
'🏸',
|
||||
'🥅',
|
||||
'🏒',
|
||||
'🏑',
|
||||
'🥍',
|
||||
'🏏',
|
||||
'⛳️',
|
||||
'🏹️',
|
||||
'🎣️',
|
||||
'🥊',
|
||||
'🥋',
|
||||
'🎽',
|
||||
'⛸',
|
||||
'🥌',
|
||||
'🛷',
|
||||
'🛹',
|
||||
'🎿',
|
||||
'⛷',
|
||||
'🏂',
|
||||
'🏋️♀️',
|
||||
'🏋🏼♀️',
|
||||
'🏋🏽♀️️',
|
||||
'🏋🏾♀️️️',
|
||||
'🏋🏿♀️️️️',
|
||||
'🏋️♂️️️️',
|
||||
'🏋🏻♂️️️️',
|
||||
'🏋🏼♂️️️️',
|
||||
'🏋🏽♂️️️️',
|
||||
'🏋🏾♂️️️️',
|
||||
'🏋🏿♂️️️️',
|
||||
'🤼♀️️️️',
|
||||
'🤼♂️️️️',
|
||||
'🤸♀️️️️',
|
||||
'🤸🏻♀️️️️',
|
||||
'🤸🏼♀️️️️',
|
||||
'🤸🏽♀️️️️',
|
||||
'🤸🏿♀️️️️️',
|
||||
'🤸♂️️️️',
|
||||
'🤸🏻♂️️️️',
|
||||
'🤸🏼♂️️️️️',
|
||||
'🤸🏽♂️️️️️️',
|
||||
'🤸🏾♂️️️️️️',
|
||||
'🤸🏿♂️️️️️️',
|
||||
'⛹️♀️️️️️️',
|
||||
'⛹🏻♀️️️️️️️',
|
||||
'⛹🏼♀️️️️️️️️',
|
||||
'⛹🏽♀️️️️️️️️️',
|
||||
'⛹🏾♀️️️️️️️️️️',
|
||||
'⛹🏿♀️️️️️️️️️️️',
|
||||
'⛹️♂️️️️️️️️️️️️',
|
||||
'⛹🏻♂️️️️️️️️️️️️️',
|
||||
'⛹🏼♂️️️️️️️️️️️️️️',
|
||||
'⛹🏽♂️️️️️️️️️️️️️️️',
|
||||
'⛹🏾♂️️️️️️️️️️️️️️️️',
|
||||
'⛹🏿♂️',
|
||||
'🤺️',
|
||||
'🤾♀️',
|
||||
'🤾🏻♀️️',
|
||||
'🤾🏼♀️️️',
|
||||
'🤾🏾♀️️️️',
|
||||
];
|
||||
|
|
@ -1,173 +0,0 @@
|
|||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter/services.dart';
|
||||
import 'package:font_awesome_flutter/font_awesome_flutter.dart';
|
||||
import 'package:twonly/src/views/camera/image_editor/action_button.dart';
|
||||
import 'package:twonly/src/views/camera/image_editor/data/layer.dart';
|
||||
|
||||
/// Emoji layer
|
||||
class EmojiLayer extends StatefulWidget {
|
||||
const EmojiLayer({
|
||||
required this.layerData,
|
||||
super.key,
|
||||
this.onUpdate,
|
||||
});
|
||||
final EmojiLayerData layerData;
|
||||
final VoidCallback? onUpdate;
|
||||
|
||||
@override
|
||||
State<EmojiLayer> createState() => _EmojiLayerState();
|
||||
}
|
||||
|
||||
class _EmojiLayerState extends State<EmojiLayer> {
|
||||
double initialRotation = 0;
|
||||
Offset initialOffset = Offset.zero;
|
||||
Offset initialFocalPoint = Offset.zero;
|
||||
double initialScale = 1;
|
||||
bool deleteLayer = false;
|
||||
bool twoPointerWhereDown = false;
|
||||
final GlobalKey outlineKey = GlobalKey();
|
||||
final GlobalKey emojiKey = GlobalKey();
|
||||
int pointers = 0;
|
||||
bool display = false;
|
||||
|
||||
@override
|
||||
void initState() {
|
||||
super.initState();
|
||||
|
||||
if (widget.layerData.offset.dy == 0) {
|
||||
// Set the initial offset to the center of the screen
|
||||
WidgetsBinding.instance.addPostFrameCallback((_) {
|
||||
setState(() {
|
||||
widget.layerData.offset = Offset(
|
||||
MediaQuery.of(context).size.width / 2 - (153 / 2),
|
||||
MediaQuery.of(context).size.height / 2 - (153 / 2) - 100,
|
||||
);
|
||||
});
|
||||
display = true;
|
||||
});
|
||||
} else {
|
||||
display = true;
|
||||
}
|
||||
}
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
if (!display) return Container();
|
||||
if (widget.layerData.isDeleted) return Container();
|
||||
return Stack(
|
||||
key: outlineKey,
|
||||
children: [
|
||||
Positioned(
|
||||
left: widget.layerData.offset.dx,
|
||||
top: widget.layerData.offset.dy,
|
||||
child: Listener(
|
||||
onPointerUp: (details) {
|
||||
setState(() {
|
||||
pointers--;
|
||||
if (pointers == 0) {
|
||||
twoPointerWhereDown = false;
|
||||
}
|
||||
if (deleteLayer) {
|
||||
widget.layerData.isDeleted = true;
|
||||
widget.onUpdate!();
|
||||
}
|
||||
});
|
||||
},
|
||||
onPointerDown: (details) {
|
||||
setState(() {
|
||||
pointers++;
|
||||
});
|
||||
},
|
||||
child: GestureDetector(
|
||||
onScaleStart: (details) {
|
||||
initialScale = widget.layerData.size;
|
||||
initialRotation = widget.layerData.rotation;
|
||||
initialOffset = widget.layerData.offset;
|
||||
initialFocalPoint =
|
||||
Offset(details.focalPoint.dx, details.focalPoint.dy);
|
||||
|
||||
setState(() {});
|
||||
},
|
||||
onScaleUpdate: (details) async {
|
||||
if (twoPointerWhereDown && details.pointerCount != 2) {
|
||||
return;
|
||||
}
|
||||
final outlineBox =
|
||||
outlineKey.currentContext!.findRenderObject()! as RenderBox;
|
||||
|
||||
final emojiBox =
|
||||
emojiKey.currentContext!.findRenderObject()! as RenderBox;
|
||||
|
||||
final isAtTheBottom =
|
||||
(widget.layerData.offset.dy + emojiBox.size.height / 2) >
|
||||
outlineBox.size.height - 80;
|
||||
final isInTheCenter = MediaQuery.of(context).size.width / 2 -
|
||||
30 <
|
||||
(widget.layerData.offset.dx +
|
||||
emojiBox.size.width / 2) &&
|
||||
MediaQuery.of(context).size.width / 2 + 20 >
|
||||
(widget.layerData.offset.dx + emojiBox.size.width / 2);
|
||||
|
||||
if (isAtTheBottom && isInTheCenter) {
|
||||
if (!deleteLayer) {
|
||||
await HapticFeedback.heavyImpact();
|
||||
}
|
||||
deleteLayer = true;
|
||||
} else {
|
||||
deleteLayer = false;
|
||||
}
|
||||
setState(() {
|
||||
twoPointerWhereDown = details.pointerCount >= 2;
|
||||
widget.layerData.size = initialScale * details.scale;
|
||||
if (widget.layerData.size > 96) {
|
||||
// https://github.com/twonlyapp/twonly-app/issues/349
|
||||
widget.layerData.size = 96;
|
||||
}
|
||||
// print(widget.layerData.size);
|
||||
widget.layerData.rotation =
|
||||
initialRotation + details.rotation;
|
||||
|
||||
// Update the position based on the translation
|
||||
final dx = (initialOffset.dx) +
|
||||
(details.focalPoint.dx - initialFocalPoint.dx);
|
||||
final dy = (initialOffset.dy) +
|
||||
(details.focalPoint.dy - initialFocalPoint.dy);
|
||||
widget.layerData.offset = Offset(dx, dy);
|
||||
});
|
||||
},
|
||||
child: Transform.rotate(
|
||||
angle: widget.layerData.rotation,
|
||||
key: emojiKey,
|
||||
child: Container(
|
||||
padding: const EdgeInsets.all(44),
|
||||
color: Colors.transparent,
|
||||
child: Text(
|
||||
widget.layerData.text,
|
||||
style: TextStyle(
|
||||
fontSize: widget.layerData.size,
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
if (pointers > 0)
|
||||
Positioned(
|
||||
left: 0,
|
||||
right: 0,
|
||||
bottom: 20,
|
||||
child: Center(
|
||||
child: GestureDetector(
|
||||
child: ActionButton(
|
||||
FontAwesomeIcons.trashCan,
|
||||
tooltipText: '',
|
||||
color: deleteLayer ? Colors.red : Colors.white,
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
],
|
||||
);
|
||||
}
|
||||
}
|
||||
|
|
@ -13,7 +13,7 @@ import 'package:twonly/src/services/api/mediafiles/upload.service.dart';
|
|||
import 'package:twonly/src/services/flame.service.dart';
|
||||
import 'package:twonly/src/services/mediafiles/mediafile.service.dart';
|
||||
import 'package:twonly/src/utils/misc.dart';
|
||||
import 'package:twonly/src/views/camera/share_image_components/best_friends_selector.dart';
|
||||
import 'package:twonly/src/views/camera/share_image_contact_selection/best_friends_selector.dart';
|
||||
import 'package:twonly/src/views/components/avatar_icon.component.dart';
|
||||
import 'package:twonly/src/views/components/flame.dart';
|
||||
import 'package:twonly/src/views/components/headline.dart';
|
||||
|
|
@ -2,6 +2,7 @@
|
|||
|
||||
import 'dart:async';
|
||||
import 'dart:collection';
|
||||
|
||||
import 'package:drift/drift.dart' show Value;
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter/services.dart';
|
||||
|
|
@ -18,13 +19,13 @@ import 'package:twonly/src/utils/screenshot.dart';
|
|||
import 'package:twonly/src/utils/storage.dart';
|
||||
import 'package:twonly/src/views/camera/camera_preview_components/main_camera_controller.dart';
|
||||
import 'package:twonly/src/views/camera/camera_preview_components/save_to_gallery.dart';
|
||||
import 'package:twonly/src/views/camera/image_editor/action_button.dart';
|
||||
import 'package:twonly/src/views/camera/image_editor/data/image_item.dart';
|
||||
import 'package:twonly/src/views/camera/image_editor/data/layer.dart';
|
||||
import 'package:twonly/src/views/camera/image_editor/layers_viewer.dart';
|
||||
import 'package:twonly/src/views/camera/image_editor/modules/all_emojis.dart';
|
||||
import 'package:twonly/src/views/camera/share_image_components/select_show_time.dart';
|
||||
import 'package:twonly/src/views/camera/share_image_view.dart';
|
||||
import 'package:twonly/src/views/camera/share_image_contact_selection.view.dart';
|
||||
import 'package:twonly/src/views/camera/share_image_contact_selection/select_show_time.dart';
|
||||
import 'package:twonly/src/views/camera/share_image_editor/action_button.dart';
|
||||
import 'package:twonly/src/views/camera/share_image_editor/data/image_item.dart';
|
||||
import 'package:twonly/src/views/camera/share_image_editor/data/layer.dart';
|
||||
import 'package:twonly/src/views/camera/share_image_editor/layers_viewer.dart';
|
||||
import 'package:twonly/src/views/components/emoji_picker.bottom.dart';
|
||||
import 'package:twonly/src/views/components/media_view_sizing.dart';
|
||||
import 'package:twonly/src/views/components/notification_badge.dart';
|
||||
import 'package:video_player/video_player.dart';
|
||||
|
|
@ -2,7 +2,7 @@
|
|||
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:hand_signature/signature.dart';
|
||||
import 'package:twonly/src/views/camera/image_editor/data/image_item.dart';
|
||||
import 'package:twonly/src/views/camera/share_image_editor/data/image_item.dart';
|
||||
|
||||
/// Layer class with some common properties
|
||||
class Layer {
|
||||
|
|
@ -51,7 +51,7 @@ class EmojiLayerData extends Layer {
|
|||
EmojiLayerData({
|
||||
required super.key,
|
||||
this.text = '',
|
||||
this.size = 64,
|
||||
this.size = 94,
|
||||
super.offset,
|
||||
super.opacity,
|
||||
super.rotation,
|
||||
|
|
@ -1,5 +1,5 @@
|
|||
import 'package:flutter/material.dart';
|
||||
import 'package:twonly/src/views/camera/image_editor/data/layer.dart';
|
||||
import 'package:twonly/src/views/camera/share_image_editor/data/layer.dart';
|
||||
|
||||
class BackgroundLayer extends StatefulWidget {
|
||||
const BackgroundLayer({
|
||||
|
|
@ -1,12 +1,11 @@
|
|||
import 'package:flutter/material.dart';
|
||||
import 'package:font_awesome_flutter/font_awesome_flutter.dart';
|
||||
import 'package:hand_signature/signature.dart';
|
||||
// ignore: implementation_imports
|
||||
import 'package:hand_signature/src/utils.dart';
|
||||
|
||||
import 'package:twonly/src/utils/misc.dart';
|
||||
import 'package:twonly/src/utils/screenshot.dart';
|
||||
import 'package:twonly/src/views/camera/image_editor/action_button.dart';
|
||||
import 'package:twonly/src/views/camera/image_editor/data/layer.dart';
|
||||
import 'package:twonly/src/views/camera/share_image_editor/action_button.dart';
|
||||
import 'package:twonly/src/views/camera/share_image_editor/data/layer.dart';
|
||||
import 'package:twonly/src/views/camera/share_image_editor/layers/draw/custom_hand_signature.dart';
|
||||
|
||||
class DrawLayer extends StatefulWidget {
|
||||
const DrawLayer({
|
||||
|
|
@ -23,8 +22,6 @@ class DrawLayer extends StatefulWidget {
|
|||
class _DrawLayerState extends State<DrawLayer> {
|
||||
Color currentColor = Colors.red;
|
||||
|
||||
ScreenshotController screenshotController = ScreenshotController();
|
||||
|
||||
List<CubicPath> undoList = [];
|
||||
bool skipNextEvent = false;
|
||||
bool showMagnifyingGlass = false;
|
||||
|
|
@ -85,17 +82,11 @@ class _DrawLayerState extends State<DrawLayer> {
|
|||
fit: StackFit.expand,
|
||||
children: [
|
||||
Positioned.fill(
|
||||
child: Container(
|
||||
decoration: const BoxDecoration(
|
||||
color: Colors.transparent,
|
||||
),
|
||||
child: Screenshot(
|
||||
controller: screenshotController,
|
||||
child: HandSignature(
|
||||
child: CustomHandSignature(
|
||||
control: widget.layerData.control,
|
||||
drawer: CustomSignatureDrawer(color: currentColor, width: 7),
|
||||
),
|
||||
),
|
||||
isModificationEnabled: widget.layerData.isEditing,
|
||||
currentColor: currentColor,
|
||||
width: 7,
|
||||
),
|
||||
),
|
||||
if (widget.layerData.isEditing && widget.layerData.showCustomButtons)
|
||||
|
|
@ -211,12 +202,12 @@ class _DrawLayerState extends State<DrawLayer> {
|
|||
top: 50 + (185 * _sliderValue),
|
||||
child: MagnifyingGlass(color: currentColor),
|
||||
),
|
||||
if (!widget.layerData.isEditing)
|
||||
Positioned.fill(
|
||||
child: Container(
|
||||
color: Colors.transparent,
|
||||
),
|
||||
),
|
||||
// if (!widget.layerData.isEditing)
|
||||
// Positioned.fill(
|
||||
// child: Container(
|
||||
// color: Colors.transparent,
|
||||
// ),
|
||||
// ),
|
||||
],
|
||||
);
|
||||
}
|
||||
|
|
@ -244,33 +235,3 @@ class MagnifyingGlass extends StatelessWidget {
|
|||
);
|
||||
}
|
||||
}
|
||||
|
||||
class CustomSignatureDrawer extends HandSignatureDrawer {
|
||||
const CustomSignatureDrawer({
|
||||
this.width = 1.0,
|
||||
this.color = Colors.black,
|
||||
});
|
||||
final Color color;
|
||||
final double width;
|
||||
|
||||
@override
|
||||
void paint(Canvas canvas, Size size, List<CubicPath> paths) {
|
||||
for (final path in paths) {
|
||||
var lineColor = color;
|
||||
if (path.setup.args!['color'] != null) {
|
||||
lineColor = path.setup.args!['color'] as Color;
|
||||
} else {
|
||||
path.setup.args!['color'] = color;
|
||||
}
|
||||
final paint = Paint()
|
||||
..color = lineColor
|
||||
..style = PaintingStyle.stroke
|
||||
..strokeCap = StrokeCap.round
|
||||
..strokeJoin = StrokeJoin.round
|
||||
..strokeWidth = width;
|
||||
if (path.isFilled) {
|
||||
canvas.drawPath(PathUtil.toLinePath(path.lines), paint);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,77 @@
|
|||
import 'package:flutter/material.dart';
|
||||
import 'package:hand_signature/signature.dart';
|
||||
// ignore: implementation_imports
|
||||
import 'package:hand_signature/src/utils.dart';
|
||||
|
||||
class CustomHandSignature extends StatelessWidget {
|
||||
const CustomHandSignature({
|
||||
required this.control,
|
||||
required this.isModificationEnabled,
|
||||
required this.currentColor,
|
||||
required this.width,
|
||||
super.key,
|
||||
});
|
||||
|
||||
/// The controller that manages the creation and manipulation of signature paths.
|
||||
final HandSignatureControl control;
|
||||
final bool isModificationEnabled;
|
||||
final Color currentColor;
|
||||
final double width;
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
control.params = SignaturePaintParams(
|
||||
color: currentColor,
|
||||
strokeWidth: 7,
|
||||
);
|
||||
|
||||
final drawer = CustomSignatureDrawer(color: currentColor, width: width);
|
||||
|
||||
if (isModificationEnabled) {
|
||||
return HandSignature(
|
||||
control: control,
|
||||
drawer: drawer,
|
||||
);
|
||||
}
|
||||
|
||||
return IgnorePointer(
|
||||
child: ClipRRect(
|
||||
child: HandSignaturePaint(
|
||||
control: control,
|
||||
drawer: drawer,
|
||||
onSize: control.notifyDimension,
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
class CustomSignatureDrawer extends HandSignatureDrawer {
|
||||
const CustomSignatureDrawer({
|
||||
this.width = 1.0,
|
||||
this.color = Colors.black,
|
||||
});
|
||||
final Color color;
|
||||
final double width;
|
||||
|
||||
@override
|
||||
void paint(Canvas canvas, Size size, List<CubicPath> paths) {
|
||||
for (final path in paths) {
|
||||
var lineColor = color;
|
||||
if (path.setup.args!['color'] != null) {
|
||||
lineColor = path.setup.args!['color'] as Color;
|
||||
} else {
|
||||
path.setup.args!['color'] = color;
|
||||
}
|
||||
final paint = Paint()
|
||||
..color = lineColor
|
||||
..style = PaintingStyle.stroke
|
||||
..strokeCap = StrokeCap.round
|
||||
..strokeJoin = StrokeJoin.round
|
||||
..strokeWidth = width;
|
||||
if (path.isFilled) {
|
||||
canvas.drawPath(PathUtil.toLinePath(path.lines), paint);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
248
lib/src/views/camera/share_image_editor/layers/emoji.layer.dart
Executable file
248
lib/src/views/camera/share_image_editor/layers/emoji.layer.dart
Executable file
|
|
@ -0,0 +1,248 @@
|
|||
import 'dart:ui' as ui;
|
||||
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter/rendering.dart';
|
||||
import 'package:flutter/services.dart';
|
||||
import 'package:font_awesome_flutter/font_awesome_flutter.dart';
|
||||
import 'package:twonly/src/utils/log.dart';
|
||||
import 'package:twonly/src/views/camera/share_image_editor/action_button.dart';
|
||||
import 'package:twonly/src/views/camera/share_image_editor/data/layer.dart';
|
||||
|
||||
/// Emoji layer
|
||||
class EmojiLayer extends StatefulWidget {
|
||||
const EmojiLayer({
|
||||
required this.layerData,
|
||||
super.key,
|
||||
this.onUpdate,
|
||||
});
|
||||
final EmojiLayerData layerData;
|
||||
final VoidCallback? onUpdate;
|
||||
|
||||
@override
|
||||
State<EmojiLayer> createState() => _EmojiLayerState();
|
||||
}
|
||||
|
||||
class _EmojiLayerState extends State<EmojiLayer> {
|
||||
double initialRotation = 0;
|
||||
Offset initialOffset = Offset.zero;
|
||||
Offset initialFocalPoint = Offset.zero;
|
||||
double initialScale = 1;
|
||||
bool deleteLayer = false;
|
||||
bool twoPointerWhereDown = false;
|
||||
final GlobalKey outlineKey = GlobalKey();
|
||||
final GlobalKey emojiKey = GlobalKey();
|
||||
int pointers = 0;
|
||||
bool display = false;
|
||||
|
||||
@override
|
||||
void initState() {
|
||||
super.initState();
|
||||
|
||||
if (widget.layerData.offset.dy == 0) {
|
||||
// Set the initial offset to the center of the screen
|
||||
WidgetsBinding.instance.addPostFrameCallback((_) {
|
||||
setState(() {
|
||||
widget.layerData.offset = Offset(
|
||||
MediaQuery.of(context).size.width / 2 - (153 / 2),
|
||||
MediaQuery.of(context).size.height / 2 - (153 / 2) - 100,
|
||||
);
|
||||
});
|
||||
display = true;
|
||||
});
|
||||
} else {
|
||||
display = true;
|
||||
}
|
||||
}
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
if (!display) return Container();
|
||||
if (widget.layerData.isDeleted) return Container();
|
||||
return Stack(
|
||||
key: outlineKey,
|
||||
children: [
|
||||
Positioned(
|
||||
left: widget.layerData.offset.dx,
|
||||
top: widget.layerData.offset.dy,
|
||||
child: PhysicalModel(
|
||||
color: Colors.transparent,
|
||||
borderRadius: BorderRadius.circular(180),
|
||||
clipBehavior: Clip.antiAlias,
|
||||
child: Listener(
|
||||
onPointerUp: (details) {
|
||||
setState(() {
|
||||
pointers--;
|
||||
if (pointers == 0) {
|
||||
twoPointerWhereDown = false;
|
||||
}
|
||||
if (deleteLayer) {
|
||||
widget.layerData.isDeleted = true;
|
||||
widget.onUpdate!();
|
||||
}
|
||||
});
|
||||
},
|
||||
onPointerDown: (details) {
|
||||
setState(() {
|
||||
pointers++;
|
||||
});
|
||||
},
|
||||
child: GestureDetector(
|
||||
onScaleStart: (details) {
|
||||
initialScale = widget.layerData.size;
|
||||
initialRotation = widget.layerData.rotation;
|
||||
initialOffset = widget.layerData.offset;
|
||||
initialFocalPoint =
|
||||
Offset(details.focalPoint.dx, details.focalPoint.dy);
|
||||
|
||||
setState(() {});
|
||||
},
|
||||
onScaleUpdate: (details) async {
|
||||
if (twoPointerWhereDown && details.pointerCount != 2) {
|
||||
return;
|
||||
}
|
||||
final outlineBox = outlineKey.currentContext!
|
||||
.findRenderObject()! as RenderBox;
|
||||
|
||||
final emojiBox =
|
||||
emojiKey.currentContext!.findRenderObject()! as RenderBox;
|
||||
|
||||
final isAtTheBottom =
|
||||
(widget.layerData.offset.dy + emojiBox.size.height / 2) >
|
||||
outlineBox.size.height - 80;
|
||||
final isInTheCenter =
|
||||
MediaQuery.of(context).size.width / 2 - 30 <
|
||||
(widget.layerData.offset.dx +
|
||||
emojiBox.size.width / 2) &&
|
||||
MediaQuery.of(context).size.width / 2 + 20 >
|
||||
(widget.layerData.offset.dx +
|
||||
emojiBox.size.width / 2);
|
||||
|
||||
if (isAtTheBottom && isInTheCenter) {
|
||||
if (!deleteLayer) {
|
||||
await HapticFeedback.heavyImpact();
|
||||
}
|
||||
deleteLayer = true;
|
||||
} else {
|
||||
deleteLayer = false;
|
||||
}
|
||||
setState(() {
|
||||
twoPointerWhereDown = details.pointerCount >= 2;
|
||||
widget.layerData.size = initialScale * details.scale;
|
||||
// print(widget.layerData.size);
|
||||
widget.layerData.rotation =
|
||||
initialRotation + details.rotation;
|
||||
|
||||
// Update the position based on the translation
|
||||
final dx = (initialOffset.dx) +
|
||||
(details.focalPoint.dx - initialFocalPoint.dx);
|
||||
final dy = (initialOffset.dy) +
|
||||
(details.focalPoint.dy - initialFocalPoint.dy);
|
||||
widget.layerData.offset = Offset(dx, dy);
|
||||
});
|
||||
},
|
||||
child: Transform.rotate(
|
||||
angle: widget.layerData.rotation,
|
||||
key: emojiKey,
|
||||
child: Container(
|
||||
padding: const EdgeInsets.all(44),
|
||||
color: Colors.transparent,
|
||||
child: ScreenshotEmoji(
|
||||
emoji: widget.layerData.text,
|
||||
displaySize: widget.layerData.size,
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
if (pointers > 0)
|
||||
Positioned(
|
||||
left: 0,
|
||||
right: 0,
|
||||
bottom: 20,
|
||||
child: Center(
|
||||
child: GestureDetector(
|
||||
child: ActionButton(
|
||||
FontAwesomeIcons.trashCan,
|
||||
tooltipText: '',
|
||||
color: deleteLayer ? Colors.red : Colors.white,
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
],
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
// Workaround: https://github.com/twonlyapp/twonly-app/issues/349
|
||||
class ScreenshotEmoji extends StatefulWidget {
|
||||
const ScreenshotEmoji({
|
||||
required this.emoji,
|
||||
required this.displaySize,
|
||||
super.key,
|
||||
});
|
||||
final String emoji;
|
||||
final double displaySize;
|
||||
|
||||
@override
|
||||
State<ScreenshotEmoji> createState() => _ScreenshotEmojiState();
|
||||
}
|
||||
|
||||
class _ScreenshotEmojiState extends State<ScreenshotEmoji> {
|
||||
final GlobalKey _boundaryKey = GlobalKey();
|
||||
ui.Image? _capturedImage;
|
||||
|
||||
@override
|
||||
void initState() {
|
||||
super.initState();
|
||||
// Capture the emoji immediately after the first frame
|
||||
WidgetsBinding.instance.addPostFrameCallback((_) => _captureEmoji());
|
||||
}
|
||||
|
||||
Future<void> _captureEmoji() async {
|
||||
try {
|
||||
final boundary = _boundaryKey.currentContext?.findRenderObject()
|
||||
as RenderRepaintBoundary?;
|
||||
if (boundary == null) return;
|
||||
|
||||
final image = await boundary.toImage(pixelRatio: 4);
|
||||
setState(() {
|
||||
_capturedImage = image;
|
||||
});
|
||||
} catch (e) {
|
||||
Log.error('Error capturing emoji: $e');
|
||||
}
|
||||
}
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
if (_capturedImage != null) {
|
||||
return SizedBox(
|
||||
width: widget.displaySize,
|
||||
height: widget.displaySize,
|
||||
child: RawImage(
|
||||
image: _capturedImage,
|
||||
fit: BoxFit.contain,
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
return Stack(
|
||||
children: [
|
||||
Positioned(
|
||||
top: -200, // hide from the user as the size changes with the image
|
||||
child: RepaintBoundary(
|
||||
key: _boundaryKey,
|
||||
child: Text(
|
||||
widget.emoji,
|
||||
style: const TextStyle(fontSize: 94),
|
||||
),
|
||||
),
|
||||
),
|
||||
SizedBox(width: widget.displaySize, height: widget.displaySize),
|
||||
],
|
||||
);
|
||||
}
|
||||
}
|
||||
|
|
@ -2,10 +2,10 @@ import 'dart:async';
|
|||
|
||||
import 'package:collection/collection.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:twonly/src/views/camera/image_editor/data/layer.dart';
|
||||
import 'package:twonly/src/views/camera/image_editor/layers/filters/datetime_filter.dart';
|
||||
import 'package:twonly/src/views/camera/image_editor/layers/filters/image_filter.dart';
|
||||
import 'package:twonly/src/views/camera/image_editor/layers/filters/location_filter.dart';
|
||||
import 'package:twonly/src/views/camera/share_image_editor/data/layer.dart';
|
||||
import 'package:twonly/src/views/camera/share_image_editor/layers/filters/datetime_filter.dart';
|
||||
import 'package:twonly/src/views/camera/share_image_editor/layers/filters/image_filter.dart';
|
||||
import 'package:twonly/src/views/camera/share_image_editor/layers/filters/location_filter.dart';
|
||||
|
||||
/// Main layer
|
||||
class FilterLayer extends StatefulWidget {
|
||||
|
|
@ -1,7 +1,7 @@
|
|||
import 'package:clock/clock.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:intl/intl.dart';
|
||||
import 'package:twonly/src/views/camera/image_editor/layers/filter_layer.dart';
|
||||
import 'package:twonly/src/views/camera/share_image_editor/layers/filter.layer.dart';
|
||||
|
||||
class DateTimeFilter extends StatelessWidget {
|
||||
const DateTimeFilter({super.key, this.color = Colors.white});
|
||||
|
|
@ -1,6 +1,6 @@
|
|||
import 'package:cached_network_image/cached_network_image.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:twonly/src/views/camera/image_editor/layers/filter_layer.dart';
|
||||
import 'package:twonly/src/views/camera/share_image_editor/layers/filter.layer.dart';
|
||||
|
||||
class ImageFilter extends StatelessWidget {
|
||||
const ImageFilter({required this.imagePath, super.key});
|
||||
|
|
@ -11,8 +11,8 @@ import 'package:path_provider/path_provider.dart';
|
|||
import 'package:twonly/globals.dart';
|
||||
import 'package:twonly/src/model/protobuf/api/websocket/server_to_client.pb.dart';
|
||||
import 'package:twonly/src/utils/log.dart';
|
||||
import 'package:twonly/src/views/camera/image_editor/layers/filter_layer.dart';
|
||||
import 'package:twonly/src/views/camera/image_editor/layers/filters/datetime_filter.dart';
|
||||
import 'package:twonly/src/views/camera/share_image_editor/layers/filter.layer.dart';
|
||||
import 'package:twonly/src/views/camera/share_image_editor/layers/filters/datetime_filter.dart';
|
||||
|
||||
class LocationFilter extends StatefulWidget {
|
||||
const LocationFilter({super.key});
|
||||
|
|
@ -5,8 +5,8 @@ import 'package:flutter/services.dart';
|
|||
import 'package:font_awesome_flutter/font_awesome_flutter.dart';
|
||||
import 'package:provider/provider.dart';
|
||||
import 'package:twonly/src/providers/image_editor.provider.dart';
|
||||
import 'package:twonly/src/views/camera/image_editor/action_button.dart';
|
||||
import 'package:twonly/src/views/camera/image_editor/data/layer.dart';
|
||||
import 'package:twonly/src/views/camera/share_image_editor/action_button.dart';
|
||||
import 'package:twonly/src/views/camera/share_image_editor/data/layer.dart';
|
||||
|
||||
/// Text layer
|
||||
class TextLayer extends StatefulWidget {
|
||||
|
|
@ -1,10 +1,10 @@
|
|||
import 'package:flutter/material.dart';
|
||||
import 'package:twonly/src/views/camera/image_editor/data/layer.dart';
|
||||
import 'package:twonly/src/views/camera/image_editor/layers/background_layer.dart';
|
||||
import 'package:twonly/src/views/camera/image_editor/layers/draw_layer.dart';
|
||||
import 'package:twonly/src/views/camera/image_editor/layers/emoji_layer.dart';
|
||||
import 'package:twonly/src/views/camera/image_editor/layers/filter_layer.dart';
|
||||
import 'package:twonly/src/views/camera/image_editor/layers/text_layer.dart';
|
||||
import 'package:twonly/src/views/camera/share_image_editor/data/layer.dart';
|
||||
import 'package:twonly/src/views/camera/share_image_editor/layers/background.layer.dart';
|
||||
import 'package:twonly/src/views/camera/share_image_editor/layers/draw.layer.dart';
|
||||
import 'package:twonly/src/views/camera/share_image_editor/layers/emoji.layer.dart';
|
||||
import 'package:twonly/src/views/camera/share_image_editor/layers/filter.layer.dart';
|
||||
import 'package:twonly/src/views/camera/share_image_editor/layers/text.layer.dart';
|
||||
|
||||
/// View stacked layers (unbounded height, width)
|
||||
class LayersViewer extends StatelessWidget {
|
||||
|
|
@ -37,7 +37,9 @@ class LayersViewer extends StatelessWidget {
|
|||
...layers
|
||||
.where(
|
||||
(layerItem) =>
|
||||
layerItem is EmojiLayerData || layerItem is DrawLayerData,
|
||||
layerItem is EmojiLayerData ||
|
||||
layerItem is DrawLayerData ||
|
||||
layerItem is TextLayerData,
|
||||
)
|
||||
.map((layerItem) {
|
||||
if (layerItem is EmojiLayerData) {
|
||||
|
|
@ -52,15 +54,14 @@ class LayersViewer extends StatelessWidget {
|
|||
layerData: layerItem,
|
||||
onUpdate: onUpdate,
|
||||
);
|
||||
}
|
||||
return Container();
|
||||
}),
|
||||
...layers.whereType<TextLayerData>().map((layerItem) {
|
||||
} else if (layerItem is TextLayerData) {
|
||||
return TextLayer(
|
||||
key: layerItem.key,
|
||||
layerData: layerItem,
|
||||
onUpdate: onUpdate,
|
||||
);
|
||||
}
|
||||
return Container();
|
||||
}),
|
||||
],
|
||||
);
|
||||
|
|
@ -9,7 +9,7 @@ import 'package:twonly/src/database/tables/messages.table.dart';
|
|||
import 'package:twonly/src/database/twonly.db.dart';
|
||||
import 'package:twonly/src/services/api/mediafiles/download.service.dart';
|
||||
import 'package:twonly/src/utils/misc.dart';
|
||||
import 'package:twonly/src/views/camera/camera_send_to_view.dart';
|
||||
import 'package:twonly/src/views/camera/camera_send_to.view.dart';
|
||||
import 'package:twonly/src/views/chats/chat_list_components/last_message_time.dart';
|
||||
import 'package:twonly/src/views/chats/chat_messages.view.dart';
|
||||
import 'package:twonly/src/views/chats/chat_messages_components/message_send_state_icon.dart';
|
||||
|
|
|
|||
|
|
@ -14,8 +14,8 @@ import 'package:twonly/src/model/protobuf/client/generated/messages.pbserver.dar
|
|||
import 'package:twonly/src/services/api/messages.dart';
|
||||
import 'package:twonly/src/services/mediafiles/mediafile.service.dart';
|
||||
import 'package:twonly/src/utils/misc.dart';
|
||||
import 'package:twonly/src/views/camera/image_editor/data/layer.dart';
|
||||
import 'package:twonly/src/views/camera/image_editor/modules/all_emojis.dart';
|
||||
import 'package:twonly/src/views/camera/share_image_editor/data/layer.dart';
|
||||
import 'package:twonly/src/views/components/emoji_picker.bottom.dart';
|
||||
import 'package:twonly/src/views/chats/message_info.view.dart';
|
||||
import 'package:twonly/src/views/components/alert_dialog.dart';
|
||||
import 'package:twonly/src/views/components/context_menu.component.dart';
|
||||
|
|
|
|||
|
|
@ -14,7 +14,7 @@ import 'package:twonly/src/database/twonly.db.dart';
|
|||
import 'package:twonly/src/services/api/mediafiles/upload.service.dart';
|
||||
import 'package:twonly/src/services/api/messages.dart';
|
||||
import 'package:twonly/src/utils/misc.dart';
|
||||
import 'package:twonly/src/views/camera/camera_send_to_view.dart';
|
||||
import 'package:twonly/src/views/camera/camera_send_to.view.dart';
|
||||
import 'package:twonly/src/views/chats/chat_messages_components/entries/chat_audio_entry.dart';
|
||||
|
||||
class MessageInput extends StatefulWidget {
|
||||
|
|
|
|||
|
|
@ -112,7 +112,7 @@ class _MessageSendStateIconState extends State<MessageSendStateIcon> {
|
|||
case MessageSendState.receivedOpened:
|
||||
icon = Icon(Icons.crop_square, size: 14, color: color);
|
||||
if (message.content != null) {
|
||||
if (isEmoji(message.content!)) {
|
||||
if (isOneEmoji(message.content!)) {
|
||||
icon = Text(
|
||||
message.content!,
|
||||
style: const TextStyle(fontSize: 12),
|
||||
|
|
|
|||
|
|
@ -20,7 +20,7 @@ import 'package:twonly/src/services/mediafiles/mediafile.service.dart';
|
|||
import 'package:twonly/src/services/notifications/background.notifications.dart';
|
||||
import 'package:twonly/src/utils/log.dart';
|
||||
import 'package:twonly/src/utils/misc.dart';
|
||||
import 'package:twonly/src/views/camera/camera_send_to_view.dart';
|
||||
import 'package:twonly/src/views/camera/camera_send_to.view.dart';
|
||||
import 'package:twonly/src/views/chats/media_viewer_components/reaction_buttons.component.dart';
|
||||
import 'package:twonly/src/views/components/animate_icon.dart';
|
||||
import 'package:twonly/src/views/components/loader.dart';
|
||||
|
|
|
|||
|
|
@ -5,8 +5,8 @@ import 'package:flutter/scheduler.dart';
|
|||
import 'package:font_awesome_flutter/font_awesome_flutter.dart';
|
||||
import 'package:twonly/globals.dart';
|
||||
import 'package:twonly/src/utils/misc.dart';
|
||||
import 'package:twonly/src/views/camera/image_editor/data/layer.dart';
|
||||
import 'package:twonly/src/views/camera/image_editor/modules/all_emojis.dart';
|
||||
import 'package:twonly/src/views/camera/share_image_editor/data/layer.dart';
|
||||
import 'package:twonly/src/views/components/emoji_picker.bottom.dart';
|
||||
import 'package:twonly/src/views/chats/media_viewer_components/emoji_reactions_row.component.dart';
|
||||
import 'package:twonly/src/views/components/animate_icon.dart';
|
||||
|
||||
|
|
|
|||
File diff suppressed because one or more lines are too long
|
|
@ -2,7 +2,7 @@ import 'dart:io';
|
|||
import 'package:emoji_picker_flutter/emoji_picker_flutter.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:twonly/src/utils/misc.dart';
|
||||
import 'package:twonly/src/views/camera/image_editor/data/layer.dart';
|
||||
import 'package:twonly/src/views/camera/share_image_editor/data/layer.dart';
|
||||
|
||||
class EmojiPickerBottom extends StatelessWidget {
|
||||
const EmojiPickerBottom({super.key});
|
||||
|
|
@ -14,7 +14,7 @@ import 'package:twonly/src/utils/misc.dart';
|
|||
import 'package:twonly/src/views/camera/camera_preview_components/camera_preview.dart';
|
||||
import 'package:twonly/src/views/camera/camera_preview_components/camera_preview_controller_view.dart';
|
||||
import 'package:twonly/src/views/camera/camera_preview_components/main_camera_controller.dart';
|
||||
import 'package:twonly/src/views/camera/share_image_editor_view.dart';
|
||||
import 'package:twonly/src/views/camera/share_image_editor.view.dart';
|
||||
import 'package:twonly/src/views/chats/chat_list.view.dart';
|
||||
import 'package:twonly/src/views/memories/memories.view.dart';
|
||||
|
||||
|
|
|
|||
|
|
@ -9,7 +9,7 @@ import 'package:twonly/src/services/api/mediafiles/upload.service.dart';
|
|||
import 'package:twonly/src/utils/log.dart';
|
||||
import 'package:twonly/src/utils/misc.dart';
|
||||
import 'package:twonly/src/views/camera/camera_preview_components/save_to_gallery.dart';
|
||||
import 'package:twonly/src/views/camera/share_image_editor_view.dart';
|
||||
import 'package:twonly/src/views/camera/share_image_editor.view.dart';
|
||||
import 'package:twonly/src/views/components/alert_dialog.dart';
|
||||
import 'package:twonly/src/views/components/media_view_sizing.dart';
|
||||
import 'package:twonly/src/views/components/video_player_wrapper.dart';
|
||||
|
|
|
|||
|
|
@ -4,7 +4,7 @@ import 'package:cached_network_image/cached_network_image.dart';
|
|||
import 'package:flutter/material.dart';
|
||||
import 'package:font_awesome_flutter/font_awesome_flutter.dart';
|
||||
import 'package:twonly/src/utils/misc.dart';
|
||||
import 'package:twonly/src/views/camera/image_editor/layers/filters/location_filter.dart';
|
||||
import 'package:twonly/src/views/camera/share_image_editor/layers/filters/location_filter.dart';
|
||||
import 'package:url_launcher/url_launcher.dart';
|
||||
|
||||
class UrlListTitle extends StatelessWidget {
|
||||
|
|
|
|||
|
|
@ -8,10 +8,10 @@ import 'package:twonly/src/views/components/animate_icon.dart';
|
|||
void main() {
|
||||
group('testing utils', () {
|
||||
test('test isEmoji function', () {
|
||||
expect(isEmoji('Hallo'), false);
|
||||
expect(isEmoji('😂'), true);
|
||||
expect(isEmoji('😂😂'), false);
|
||||
expect(isEmoji('Hallo 😂'), false);
|
||||
expect(isOneEmoji('Hallo'), false);
|
||||
expect(isOneEmoji('😂'), true);
|
||||
expect(isOneEmoji('😂😂'), false);
|
||||
expect(isOneEmoji('Hallo 😂'), false);
|
||||
});
|
||||
|
||||
test('test proof-of-work simple', () async {
|
||||
|
|
|
|||
Loading…
Reference in a new issue