Skip to content

Commit

Permalink
feat: support customizing the mobile magnifiter (AppFlowy-IO#625)
Browse files Browse the repository at this point in the history
* feat: support customizing the mobile magnifiter

* chore: remove unused test

* feat: expaned the padding between cursor and toolbar

* feat: support long pressing to update selection

* chore: revert code sign
  • Loading branch information
LucasXu0 authored Dec 19, 2023
1 parent e6b1336 commit 82f3baf
Show file tree
Hide file tree
Showing 14 changed files with 209 additions and 111 deletions.
6 changes: 3 additions & 3 deletions example/ios/Runner.xcodeproj/project.pbxproj
Original file line number Diff line number Diff line change
Expand Up @@ -358,7 +358,7 @@
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
CLANG_ENABLE_MODULES = YES;
CURRENT_PROJECT_VERSION = "$(FLUTTER_BUILD_NUMBER)";
DEVELOPMENT_TEAM = KD48NC6JW7;
DEVELOPMENT_TEAM = "";
ENABLE_BITCODE = NO;
INFOPLIST_FILE = Runner/Info.plist;
LD_RUNPATH_SEARCH_PATHS = (
Expand Down Expand Up @@ -487,7 +487,7 @@
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
CLANG_ENABLE_MODULES = YES;
CURRENT_PROJECT_VERSION = "$(FLUTTER_BUILD_NUMBER)";
DEVELOPMENT_TEAM = KD48NC6JW7;
DEVELOPMENT_TEAM = "";
ENABLE_BITCODE = NO;
INFOPLIST_FILE = Runner/Info.plist;
LD_RUNPATH_SEARCH_PATHS = (
Expand All @@ -510,7 +510,7 @@
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
CLANG_ENABLE_MODULES = YES;
CURRENT_PROJECT_VERSION = "$(FLUTTER_BUILD_NUMBER)";
DEVELOPMENT_TEAM = KD48NC6JW7;
DEVELOPMENT_TEAM = "";
ENABLE_BITCODE = NO;
INFOPLIST_FILE = Runner/Info.plist;
LD_RUNPATH_SEARCH_PATHS = (
Expand Down
2 changes: 2 additions & 0 deletions example/lib/pages/mobile_editor.dart
Original file line number Diff line number Diff line change
Expand Up @@ -123,6 +123,8 @@ class _MobileEditorState extends State<MobileEditor> {
),
),
padding: const EdgeInsets.symmetric(horizontal: 24.0),
magnifierSize: const Size(144, 96),
mobileDragHandleBallSize: const Size(12, 12),
);
}

Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import 'package:appflowy_editor/appflowy_editor.dart';
import 'package:appflowy_editor/src/editor/block_component/base_component/selection/selection_area_painter.dart';
import 'package:appflowy_editor/src/editor/editor_component/service/selection/mobile_selection_service.dart';
import 'package:appflowy_editor/src/render/selection/cursor.dart';
import 'package:collection/collection.dart';
import 'package:flutter/foundation.dart';
Expand Down Expand Up @@ -123,10 +124,15 @@ class _BlockSelectionAreaState extends State<BlockSelectionArea> {
prevCursorRect == null) {
return sizedBox;
}
final editorState = context.read<EditorState>();
final dragMode =
editorState.selectionExtraInfo?[selectionDragModeKey];
final shouldBlink = widget.delegate.shouldCursorBlink &&
dragMode != MobileSelectionDragMode.cursor;
final cursor = Cursor(
key: cursorKey,
rect: prevCursorRect!,
shouldBlink: widget.delegate.shouldCursorBlink,
shouldBlink: shouldBlink,
cursorStyle: widget.delegate.cursorStyle,
color: widget.cursorColor,
);
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import 'package:appflowy_editor/appflowy_editor.dart';
import 'package:appflowy_editor/src/editor/editor_component/service/scroll/desktop_scroll_service.dart';
import 'package:appflowy_editor/src/editor/editor_component/service/scroll/mobile_scroll_service.dart';
import 'package:appflowy_editor/src/editor/toolbar/mobile/utils/keyboard_height_observer.dart';
import 'package:flutter/material.dart';
import 'package:provider/provider.dart';

Expand Down Expand Up @@ -100,10 +101,14 @@ class _ScrollServiceWidgetState extends State<ScrollServiceWidget>
if (PlatformExtension.isMobile) {
// soft keyboard
// workaround: wait for the soft keyboard to show up
return Future.delayed(const Duration(milliseconds: 300), () {
return Future.delayed(
Duration(
milliseconds:
KeyboardHeightObserver.currentKeyboardHeight == 0 ? 250 : 0,
), () {
startAutoScroll(
endTouchPoint,
edgeOffset: 100,
edgeOffset: 150,
duration: Duration.zero,
);
});
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -12,17 +12,55 @@ class MobileMagnifier extends StatelessWidget {

@override
Widget build(BuildContext context) {
// the magnifier will blink if the center is the same as the offset.
final magicOffset = Offset(0, size.height - 22);
return Positioned.fromRect(
rect: Rect.fromCenter(
center: offset.translate(0, -size.height),
center: offset - magicOffset,
width: size.width,
height: size.height,
),
child: IgnorePointer(
child: Magnifier(
child: _CustomMagnifier(
size: size,
additionalFocalPointOffset: magicOffset,
),
),
);
}
}

class _CustomMagnifier extends StatelessWidget {
const _CustomMagnifier({
this.additionalFocalPointOffset = Offset.zero,
required this.size,
});

final Size size;
final Offset additionalFocalPointOffset;

@override
Widget build(BuildContext context) {
return RawMagnifier(
decoration: const MagnifierDecoration(
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.all(Radius.circular(40)),
),
shadows: <BoxShadow>[
BoxShadow(
blurRadius: 1.5,
offset: Offset(0, 2),
spreadRadius: 0.75,
color: Color.fromARGB(25, 0, 0, 0),
),
],
),
magnificationScale: 1.25,
focalPointOffset: additionalFocalPointOffset,
size: size,
child: const ColoredBox(
color: Color.fromARGB(8, 158, 158, 158),
),
);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -19,12 +19,16 @@ enum MobileSelectionHandlerType {
cursorHandler,
}

// the value type is MobileSelectionDragMode
const String selectionDragModeKey = 'selection_drag_mode';

class MobileSelectionServiceWidget extends StatefulWidget {
const MobileSelectionServiceWidget({
super.key,
this.cursorColor = const Color(0xFF00BCF0),
this.selectionColor = const Color.fromARGB(53, 111, 201, 231),
this.showMagnifier = true,
this.magnifierSize = const Size(72, 48),
required this.child,
});

Expand All @@ -37,6 +41,8 @@ class MobileSelectionServiceWidget extends StatefulWidget {
/// only works on iOS or Android.
final bool showMagnifier;

final Size magnifierSize;

@override
State<MobileSelectionServiceWidget> createState() =>
_MobileSelectionServiceWidgetState();
Expand Down Expand Up @@ -98,6 +104,9 @@ class _MobileSelectionServiceWidgetState
onTapUp: _onTapUp,
onDoubleTapUp: _onDoubleTapUp,
onTripleTapUp: _onTripleTapUp,
onLongPressStart: _onLongPressStart,
onLongPressMoveUpdate: _onLongPressMoveUpdate,
onLongPressEnd: _onLongPressEnd,
child: Stack(
children: [
widget.child,
Expand All @@ -117,7 +126,7 @@ class _MobileSelectionServiceWidgetState
final renderBox = context.findRenderObject() as RenderBox;
final local = renderBox.globalToLocal(offset);
return MobileMagnifier(
size: const Size(72, 48),
size: widget.magnifierSize,
offset: local,
);
},
Expand Down Expand Up @@ -145,6 +154,9 @@ class _MobileSelectionServiceWidgetState
editorState.updateSelectionWithReason(
selection,
reason: SelectionUpdateReason.uiEvent,
extraInfo: {
selectionDragModeKey: dragMode,
},
);
}

Expand Down Expand Up @@ -266,7 +278,10 @@ class _MobileSelectionServiceWidgetState
return;
}

editorState.selection = Selection.collapsed(position);
editorState.updateSelectionWithReason(
Selection.collapsed(position),
extraInfo: null,
);
}

void _onDoubleTapUp(TapUpDetails details) {
Expand Down Expand Up @@ -375,6 +390,58 @@ class _MobileSelectionServiceWidgetState
void _onPanEnd(DragEndDetails details) {
// do nothing
_lastPanOffset.value = null;

// clear the status
dragMode = MobileSelectionDragMode.none;
editorState.updateSelectionWithReason(
editorState.selection,
extraInfo: null,
);
}

void _onLongPressStart(LongPressStartDetails details) {
clearSelection();

// clear old state.
_panStartOffset = null;

final position = getPositionInOffset(details.globalPosition);
if (position == null) {
return;
}

_lastPanOffset.value = details.globalPosition;

dragMode = MobileSelectionDragMode.cursor;
updateSelection(
Selection.collapsed(position),
);
}

void _onLongPressMoveUpdate(LongPressMoveUpdateDetails details) {
final panEndOffset = details.globalPosition;
final position = getNodeInOffset(panEndOffset)
?.selectable
?.getPositionInOffset(panEndOffset);

if (position == null) {
return;
}

_lastPanOffset.value = panEndOffset;
updateSelection(
Selection.collapsed(position),
);
}

void _onLongPressEnd(LongPressEndDetails details) {
_lastPanOffset.value = null;

dragMode = MobileSelectionDragMode.none;
editorState.updateSelectionWithReason(
editorState.selection,
extraInfo: null,
);
}

void _updateSelectionAreas(Selection selection) {
Expand Down Expand Up @@ -444,6 +511,9 @@ class _MobileSelectionServiceWidgetState
showLeftHandler: showLeftHandler,
showRightHandler: showRightHandler,
handlerColor: editorState.editorStyle.cursorColor,
handlerWidth: editorState.editorStyle.mobileDragHandleWidth,
handlerBallWidth:
editorState.editorStyle.mobileDragHandleBallSize.width,
),
);
_selectionAreas.add(overlay);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ import 'package:appflowy_editor/appflowy_editor.dart';
import 'package:appflowy_editor/src/editor/editor_component/service/selection/desktop_selection_service.dart';
import 'package:appflowy_editor/src/editor/editor_component/service/selection/mobile_selection_service.dart';
import 'package:flutter/material.dart' hide Overlay, OverlayEntry;
import 'package:provider/provider.dart';

class SelectionServiceWidget extends StatefulWidget {
const SelectionServiceWidget({
Expand Down Expand Up @@ -48,11 +49,13 @@ class _SelectionServiceWidgetState extends State<SelectionServiceWidget>
);
}

final editorState = context.read<EditorState>();
return MobileSelectionServiceWidget(
key: forwardKey,
cursorColor: widget.cursorColor,
selectionColor: widget.selectionColor,
showMagnifier: widget.showMagnifier,
magnifierSize: editorState.editorStyle.magnifierSize,
child: widget.child,
);
}
Expand Down
22 changes: 20 additions & 2 deletions lib/src/editor/editor_component/style/editor_style.dart
Original file line number Diff line number Diff line change
@@ -1,5 +1,4 @@
import 'package:appflowy_editor/appflowy_editor.dart';

import 'package:flutter/material.dart';

/// The style of the editor.
Expand All @@ -14,6 +13,9 @@ class EditorStyle {
required this.selectionColor,
required this.textStyleConfiguration,
required this.textSpanDecorator,
this.magnifierSize = const Size(72, 48),
this.mobileDragHandleBallSize = const Size(8, 8),
this.mobileDragHandleWidth = 2.0,
this.defaultTextDirection,
});

Expand Down Expand Up @@ -43,6 +45,16 @@ class EditorStyle {

final String? defaultTextDirection;

/// The size of the magnifier.
/// Only works on mobile.
final Size magnifierSize;

/// mobile drag handler size.
/// /// Only works on mobile.
final Size mobileDragHandleBallSize;

final double mobileDragHandleWidth;

const EditorStyle.desktop({
EdgeInsets? padding,
Color? cursorColor,
Expand All @@ -59,7 +71,10 @@ class EditorStyle {
text: TextStyle(fontSize: 16, color: Colors.black),
),
textSpanDecorator =
textSpanDecorator ?? defaultTextSpanDecoratorForAttribute;
textSpanDecorator ?? defaultTextSpanDecoratorForAttribute,
magnifierSize = Size.zero,
mobileDragHandleBallSize = Size.zero,
mobileDragHandleWidth = 0.0;

const EditorStyle.mobile({
EdgeInsets? padding,
Expand All @@ -68,6 +83,9 @@ class EditorStyle {
TextStyleConfiguration? textStyleConfiguration,
TextSpanDecoratorForAttribute? textSpanDecorator,
this.defaultTextDirection,
this.magnifierSize = const Size(72, 48),
this.mobileDragHandleBallSize = const Size(8, 8),
this.mobileDragHandleWidth = 2.0,
}) : padding = padding ?? const EdgeInsets.symmetric(horizontal: 20),
cursorColor = cursorColor ?? const Color(0xFF00BCF0),
selectionColor =
Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import 'package:appflowy_editor/appflowy_editor.dart';
import 'package:appflowy_editor/src/editor/editor_component/service/selection/mobile_selection_service.dart';
import 'package:flutter/material.dart';

class MobileFloatingToolbarItem {
Expand Down Expand Up @@ -104,8 +105,15 @@ class _MobileFloatingToolbarState extends State<MobileFloatingToolbar>
}
prevSelection = selection;
} else {
// uses debounce to avoid the computing the rects too frequently.
_clear();
final dragMode = editorState.selectionExtraInfo?[selectionDragModeKey];
if ([
MobileSelectionDragMode.leftSelectionHandler,
MobileSelectionDragMode.rightSelectionHandler,
].contains(dragMode)) {
return;
}
// uses debounce to avoid the computing the rects too frequently.
_showAfterDelay(const Duration(milliseconds: 400));
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,11 +7,13 @@ typedef KeyboardHeightCallback = void Function(double height);
class KeyboardHeightObserver {
KeyboardHeightObserver._() {
_keyboardHeightPlugin.onKeyboardHeightChanged((height) {
currentKeyboardHeight = height;
notify(height);
});
}

static final KeyboardHeightObserver instance = KeyboardHeightObserver._();
static double currentKeyboardHeight = 0;

final List<KeyboardHeightCallback> _listeners = [];
final KeyboardHeightPlugin _keyboardHeightPlugin = KeyboardHeightPlugin();
Expand Down
Loading

0 comments on commit 82f3baf

Please sign in to comment.