Skip to content

Commit

Permalink
feat: add block selection
Browse files Browse the repository at this point in the history
  • Loading branch information
LucasXu0 committed Sep 7, 2023
1 parent 2ad28d9 commit 5063495
Show file tree
Hide file tree
Showing 19 changed files with 190 additions and 50 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -78,6 +78,7 @@ class _BlockSelectionAreaState extends State<BlockSelectionArea> {
@override
Widget build(BuildContext context) {
return ValueListenableBuilder(
key: ValueKey(widget.node.id + widget.supportTypes.toString()),
valueListenable: widget.listenable,
builder: ((context, value, child) {
final sizedBox = child ?? const SizedBox.shrink();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -147,6 +147,8 @@ class _BulletedListBlockComponentWidgetState
placeholderTextStyle,
),
textDirection: textDirection,
cursorColor: editorState.editorStyle.cursorColor,
selectionColor: editorState.editorStyle.selectionColor,
),
),
],
Expand All @@ -159,6 +161,17 @@ class _BulletedListBlockComponentWidgetState
child: child,
);

child = BlockSelectionContainer(
node: node,
delegate: this,
listenable: editorState.selectionNotifier,
blockColor: editorState.editorStyle.selectionColor,
supportTypes: const [
BlockSelectionType.block,
],
child: child,
);

if (widget.showActions && widget.actionBuilder != null) {
child = BlockComponentActionWrapper(
node: node,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -97,6 +97,19 @@ class _DividerBlockComponentWidgetState
child: child,
);

final editorState = context.read<EditorState>();

child = BlockSelectionContainer(
node: node,
delegate: this,
listenable: editorState.selectionNotifier,
blockColor: editorState.editorStyle.selectionColor,
supportTypes: const [
BlockSelectionType.block,
],
child: child,
);

if (widget.showActions && widget.actionBuilder != null) {
child = BlockComponentActionWrapper(
node: node,
Expand All @@ -105,7 +118,6 @@ class _DividerBlockComponentWidgetState
);
}

final editorState = context.read<EditorState>();
final selectionNotifier = editorState.selectionNotifier;
return BlockSelectionContainer(
node: node,
Expand Down Expand Up @@ -163,7 +175,9 @@ class _DividerBlockComponentWidgetState
final dividerBox = dividerKey.currentContext?.findRenderObject();
if (parentBox is RenderBox && dividerBox is RenderBox) {
return [
dividerBox.localToGlobal(Offset.zero, ancestor: parentBox) &
(shiftWithBaseOffset
? dividerBox.localToGlobal(Offset.zero, ancestor: parentBox)
: Offset.zero) &
dividerBox.size
];
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -159,6 +159,8 @@ class _HeadingBlockComponentWidgetState
defaultTextStyle(level),
),
textDirection: textDirection,
cursorColor: editorState.editorStyle.cursorColor,
selectionColor: editorState.editorStyle.selectionColor,
),
),
],
Expand All @@ -171,6 +173,17 @@ class _HeadingBlockComponentWidgetState
child: child,
);

child = BlockSelectionContainer(
node: node,
delegate: this,
listenable: editorState.selectionNotifier,
blockColor: editorState.editorStyle.selectionColor,
supportTypes: const [
BlockSelectionType.block,
],
child: child,
);

if (widget.showActions && widget.actionBuilder != null) {
child = BlockComponentActionWrapper(
node: node,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -163,6 +163,17 @@ class ImageBlockComponentWidgetState extends State<ImageBlockComponentWidget>
child: child,
);

child = BlockSelectionContainer(
node: node,
delegate: this,
listenable: editorState.selectionNotifier,
blockColor: editorState.editorStyle.selectionColor,
supportTypes: const [
BlockSelectionType.block,
],
child: child,
);

if (widget.showActions && widget.actionBuilder != null) {
child = BlockComponentActionWrapper(
node: node,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -153,6 +153,8 @@ class _NumberedListBlockComponentWidgetState
placeholderTextStyle,
),
textDirection: textDirection,
cursorColor: editorState.editorStyle.cursorColor,
selectionColor: editorState.editorStyle.selectionColor,
),
),
],
Expand All @@ -165,6 +167,17 @@ class _NumberedListBlockComponentWidgetState
child: child,
);

child = BlockSelectionContainer(
node: node,
delegate: this,
listenable: editorState.selectionNotifier,
blockColor: editorState.editorStyle.selectionColor,
supportTypes: const [
BlockSelectionType.block,
],
child: child,
);

if (widget.showActions && widget.actionBuilder != null) {
child = BlockComponentActionWrapper(
node: node,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -144,6 +144,8 @@ class _QuoteBlockComponentWidgetState extends State<QuoteBlockComponentWidget>
placeholderTextStyle,
),
textDirection: textDirection,
cursorColor: editorState.editorStyle.cursorColor,
selectionColor: editorState.editorStyle.selectionColor,
),
),
],
Expand All @@ -157,6 +159,17 @@ class _QuoteBlockComponentWidgetState extends State<QuoteBlockComponentWidget>
child: child,
);

child = BlockSelectionContainer(
node: node,
delegate: this,
listenable: editorState.selectionNotifier,
blockColor: editorState.editorStyle.selectionColor,
supportTypes: const [
BlockSelectionType.block,
],
child: child,
);

if (widget.showActions && widget.actionBuilder != null) {
child = BlockComponentActionWrapper(
node: node,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -148,10 +148,6 @@ class _AppFlowyRichTextState extends State<AppFlowyRichText>
Position position, {
bool shiftWithBaseOffset = false,
}) {
// TODO: the debug needs layout!!!
if (_renderParagraph?.debugNeedsLayout == true) {
return null;
}
final textPosition = TextPosition(offset: position.offset);
var cursorHeight = _renderParagraph?.getFullHeightForCaret(textPosition);
var cursorOffset =
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -164,6 +164,17 @@ class _TableBlockComponentWidgetState extends State<TableBlockComponentWidget>
child: child,
);

child = BlockSelectionContainer(
node: node,
delegate: this,
listenable: editorState.selectionNotifier,
blockColor: editorState.editorStyle.selectionColor,
supportTypes: const [
BlockSelectionType.block,
],
child: child,
);

if (widget.showActions && widget.actionBuilder != null) {
child = BlockComponentActionWrapper(
node: node,
Expand Down Expand Up @@ -196,7 +207,10 @@ class _TableBlockComponentWidgetState extends State<TableBlockComponentWidget>
final tableBox = tableKey.currentContext?.findRenderObject();
if (parentBox is RenderBox && tableBox is RenderBox) {
return [
tableBox.localToGlobal(Offset.zero, ancestor: parentBox) & tableBox.size
(shiftWithBaseOffset
? tableBox.localToGlobal(Offset.zero, ancestor: parentBox)
: Offset.zero) &
tableBox.size
];
}
return [Offset.zero & _renderBox.size];
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -165,6 +165,8 @@ class _TodoListBlockComponentWidgetState
textSpan.updateTextStyle(
placeholderTextStyle,
),
cursorColor: editorState.editorStyle.cursorColor,
selectionColor: editorState.editorStyle.selectionColor,
),
),
],
Expand All @@ -177,6 +179,17 @@ class _TodoListBlockComponentWidgetState
child: child,
);

child = BlockSelectionContainer(
node: node,
delegate: this,
listenable: editorState.selectionNotifier,
blockColor: editorState.editorStyle.selectionColor,
supportTypes: const [
BlockSelectionType.block,
],
child: child,
);

if (widget.showActions && widget.actionBuilder != null) {
child = BlockComponentActionWrapper(
node: node,
Expand Down
42 changes: 20 additions & 22 deletions lib/src/editor/editor_component/entry/page_block_component.dart
Original file line number Diff line number Diff line change
Expand Up @@ -49,32 +49,30 @@ class PageBlockComponent extends BlockComponentStatelessWidget {
final editorState = context.read<EditorState>();
final scrollController = context.read<EditorScrollController>();
final items = node.children;
int extentCount = 0;
if (header != null) extentCount++;
if (footer != null) extentCount++;

if (scrollController.shrinkWrap) {
return SingleChildScrollView(
controller: scrollController.scrollController,
child: Builder(
builder: (context) {
editorState.updateAutoScroller(Scrollable.of(context));
return Padding(
padding: editorState.editorStyle.padding,
child: Column(
children: [
if (header != null) header!,
...items
.map((e) => editorState.renderer.build(context, e))
.toList(),
if (footer != null) footer!,
],
),
);
},
),
return Builder(
builder: (context) {
editorState.updateAutoScroller(Scrollable.of(context));
return Padding(
padding: editorState.editorStyle.padding,
child: Column(
children: [
if (header != null) header!,
...items
.map((e) => editorState.renderer.build(context, e))
.toList(),
if (footer != null) footer!,
],
),
);
},
);
} else {
int extentCount = 0;
if (header != null) extentCount++;
if (footer != null) extentCount++;

return ScrollablePositionedList.builder(
shrinkWrap: scrollController.shrinkWrap,
padding: editorState.editorStyle.padding,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -12,10 +12,13 @@ import 'package:scrollable_positioned_list/scrollable_positioned_list.dart';
/// You can use [offsetNotifier] to get the current scroll offset.
/// And, you can use [visibleRangeNotifier] to get the first level visible items.
///
/// If the shrinkWrap is true, the scrollController must not be null
/// and the editor should be wrapped in a SingleChildScrollView.
class EditorScrollController {
EditorScrollController({
required this.editorState,
this.shrinkWrap = false,
ScrollController? scrollController,
}) {
// if shrinkWrap is true, we will render the document with Column layout.
// otherwise, we will render the document with ScrollablePositionedList.
Expand All @@ -30,10 +33,12 @@ class EditorScrollController {
updateVisibleRange();
editorState.document.root.addListener(updateVisibleRange);

shouldDisposeScrollController = scrollController == null;
this.scrollController = scrollController ?? ScrollController();
// listen to the scroll offset
scrollController.addListener(
() => offsetNotifier.value = scrollController.offset,
);
this.scrollController.addListener(
() => offsetNotifier.value = this.scrollController.offset,
);
} else {
// listen to the scroll offset
_scrollOffsetSubscription = _scrollOffsetListener.changes.listen((value) {
Expand Down Expand Up @@ -70,7 +75,8 @@ class EditorScrollController {
// these value is required by SingleChildScrollView
// notes: don't use them if shrinkWrap is false
// ------------ start ----------------
final ScrollController scrollController = ScrollController();
late final ScrollController scrollController;
bool shouldDisposeScrollController = false;
// ------------ end ----------------

// these values are required by ScrollablePositionedList
Expand Down Expand Up @@ -132,7 +138,9 @@ class EditorScrollController {

// dispose the subscription
void dispose() {
scrollController.dispose();
if (shouldDisposeScrollController) {
scrollController.dispose();
}

_scrollOffsetSubscription.cancel();
_itemPositionsListener.itemPositions.removeListener(_listenItemPositions);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -45,10 +45,15 @@ CommandShortcutEventHandler _deleteLeftSentenceCommandHandler = (editorState) {

CommandShortcutEventHandler _backspaceCommandHandler = (editorState) {
final selection = editorState.selection;
final selectionType = editorState.selectionType;

if (selection == null) {
return KeyEventResult.ignored;
}
if (selection.isCollapsed) {

if (selectionType == SelectionType.block) {
return _backspaceInBlockSelection(editorState);
} else if (selection.isCollapsed) {
return _backspaceInCollapsedSelection(editorState);
} else {
return _backspaceInNotCollapsedSelection(editorState);
Expand Down Expand Up @@ -138,3 +143,17 @@ CommandShortcutEventHandler _backspaceInNotCollapsedSelection = (editorState) {
editorState.deleteSelection(selection);
return KeyEventResult.handled;
};

CommandShortcutEventHandler _backspaceInBlockSelection = (editorState) {
final selection = editorState.selection;
if (selection == null || editorState.selectionType != SelectionType.block) {
return KeyEventResult.ignored;
}
final transaction = editorState.transaction;
transaction.deleteNodesAtPath(selection.start.path);
editorState
.apply(transaction)
.then((value) => editorState.selectionType = null);

return KeyEventResult.handled;
};
Loading

0 comments on commit 5063495

Please sign in to comment.