mirror of
https://github.com/AppFlowy-IO/AppFlowy.git
synced 2024-08-30 18:12:39 +00:00
feat: arrow up and down
This commit is contained in:
parent
e74f5e84dc
commit
53b982e7c9
@ -63,6 +63,11 @@ class __ImageNodeWidgetState extends State<_ImageNodeWidget> with Selectable {
|
|||||||
throw UnimplementedError();
|
throw UnimplementedError();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
Offset localToGlobal(Offset offset) {
|
||||||
|
throw UnimplementedError();
|
||||||
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Rect getCursorRectInPosition(Position position) {
|
Rect getCursorRectInPosition(Position position) {
|
||||||
// TODO: implement getCursorRectInPosition
|
// TODO: implement getCursorRectInPosition
|
||||||
|
@ -70,6 +70,11 @@ class _SelectedTextNodeWidgetState extends State<_SelectedTextNodeWidget>
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
Offset localToGlobal(Offset offset) {
|
||||||
|
return _renderParagraph.localToGlobal(offset);
|
||||||
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
List<Rect> getRectsInSelection(Selection selection) {
|
List<Rect> getRectsInSelection(Selection selection) {
|
||||||
assert(pathEquals(selection.start.path, selection.end.path));
|
assert(pathEquals(selection.start.path, selection.end.path));
|
||||||
|
@ -17,10 +17,10 @@ class CursorWidget extends StatefulWidget {
|
|||||||
final LayerLink layerLink;
|
final LayerLink layerLink;
|
||||||
|
|
||||||
@override
|
@override
|
||||||
State<CursorWidget> createState() => _CursorWidgetState();
|
State<CursorWidget> createState() => CursorWidgetState();
|
||||||
}
|
}
|
||||||
|
|
||||||
class _CursorWidgetState extends State<CursorWidget> {
|
class CursorWidgetState extends State<CursorWidget> {
|
||||||
bool showCursor = true;
|
bool showCursor = true;
|
||||||
late Timer timer;
|
late Timer timer;
|
||||||
|
|
||||||
@ -28,7 +28,17 @@ class _CursorWidgetState extends State<CursorWidget> {
|
|||||||
void initState() {
|
void initState() {
|
||||||
super.initState();
|
super.initState();
|
||||||
|
|
||||||
timer = Timer.periodic(
|
timer = _initTimer();
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
void dispose() {
|
||||||
|
timer.cancel();
|
||||||
|
super.dispose();
|
||||||
|
}
|
||||||
|
|
||||||
|
Timer _initTimer() {
|
||||||
|
return Timer.periodic(
|
||||||
Duration(milliseconds: (widget.blinkingInterval * 1000).toInt()),
|
Duration(milliseconds: (widget.blinkingInterval * 1000).toInt()),
|
||||||
(timer) {
|
(timer) {
|
||||||
setState(() {
|
setState(() {
|
||||||
@ -37,10 +47,13 @@ class _CursorWidgetState extends State<CursorWidget> {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
@override
|
/// force the cursor widget to show for a while
|
||||||
void dispose() {
|
show() {
|
||||||
|
setState(() {
|
||||||
|
showCursor = true;
|
||||||
|
});
|
||||||
timer.cancel();
|
timer.cancel();
|
||||||
super.dispose();
|
timer = _initTimer();
|
||||||
}
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
|
@ -23,6 +23,8 @@ mixin Selectable<T extends StatefulWidget> on State<T> {
|
|||||||
Position getPositionInOffset(Offset start);
|
Position getPositionInOffset(Offset start);
|
||||||
Rect getCursorRectInPosition(Position position);
|
Rect getCursorRectInPosition(Position position);
|
||||||
|
|
||||||
|
Offset localToGlobal(Offset offset);
|
||||||
|
|
||||||
Position start();
|
Position start();
|
||||||
Position end();
|
Position end();
|
||||||
|
|
||||||
|
@ -2,6 +2,7 @@ import 'package:flowy_editor/document/node.dart';
|
|||||||
import 'package:flowy_editor/document/position.dart';
|
import 'package:flowy_editor/document/position.dart';
|
||||||
import 'package:flowy_editor/service/keyboard_service.dart';
|
import 'package:flowy_editor/service/keyboard_service.dart';
|
||||||
import 'package:flowy_editor/document/selection.dart';
|
import 'package:flowy_editor/document/selection.dart';
|
||||||
|
import 'package:flowy_editor/extensions/node_extensions.dart';
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
import 'package:flutter/services.dart';
|
import 'package:flutter/services.dart';
|
||||||
|
|
||||||
@ -26,7 +27,6 @@ FlowyKeyEventHandler arrowKeysHandler = (editorState, event) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (event.logicalKey == LogicalKeyboardKey.arrowLeft) {
|
if (event.logicalKey == LogicalKeyboardKey.arrowLeft) {
|
||||||
// turn left
|
|
||||||
if (currentSelection.isCollapsed) {
|
if (currentSelection.isCollapsed) {
|
||||||
final end = currentSelection.end;
|
final end = currentSelection.end;
|
||||||
final offset = end.offset;
|
final offset = end.offset;
|
||||||
@ -67,6 +67,26 @@ FlowyKeyEventHandler arrowKeysHandler = (editorState, event) {
|
|||||||
editorState.updateCursorSelection(currentSelection.collapse());
|
editorState.updateCursorSelection(currentSelection.collapse());
|
||||||
}
|
}
|
||||||
return KeyEventResult.handled;
|
return KeyEventResult.handled;
|
||||||
|
} else if (event.logicalKey == LogicalKeyboardKey.arrowUp) {
|
||||||
|
final rects = editorState.service.selectionService.rects();
|
||||||
|
if (rects.isEmpty) {
|
||||||
|
return KeyEventResult.handled;
|
||||||
|
}
|
||||||
|
final first = rects.first;
|
||||||
|
final firstOffset = Offset(first.left, first.top);
|
||||||
|
final hitOffset = firstOffset - Offset(0, first.height * 0.5);
|
||||||
|
editorState.service.selectionService.hit(hitOffset);
|
||||||
|
return KeyEventResult.handled;
|
||||||
|
} else if (event.logicalKey == LogicalKeyboardKey.arrowDown) {
|
||||||
|
final rects = editorState.service.selectionService.rects();
|
||||||
|
if (rects.isEmpty) {
|
||||||
|
return KeyEventResult.handled;
|
||||||
|
}
|
||||||
|
final first = rects.last;
|
||||||
|
final firstOffset = Offset(first.right, first.bottom);
|
||||||
|
final hitOffset = firstOffset + Offset(0, first.height * 0.5);
|
||||||
|
editorState.service.selectionService.hit(hitOffset);
|
||||||
|
return KeyEventResult.handled;
|
||||||
}
|
}
|
||||||
|
|
||||||
return KeyEventResult.ignored;
|
return KeyEventResult.ignored;
|
||||||
|
@ -1,6 +1,7 @@
|
|||||||
import 'package:flowy_editor/document/node.dart';
|
import 'package:flowy_editor/document/node.dart';
|
||||||
import 'package:flowy_editor/document/position.dart';
|
import 'package:flowy_editor/document/position.dart';
|
||||||
import 'package:flowy_editor/document/selection.dart';
|
import 'package:flowy_editor/document/selection.dart';
|
||||||
|
import 'package:flowy_editor/render/selection/selectable.dart';
|
||||||
import 'package:flowy_editor/render/selection/cursor_widget.dart';
|
import 'package:flowy_editor/render/selection/cursor_widget.dart';
|
||||||
import 'package:flowy_editor/render/selection/flowy_selection_widget.dart';
|
import 'package:flowy_editor/render/selection/flowy_selection_widget.dart';
|
||||||
import 'package:flowy_editor/extensions/object_extensions.dart';
|
import 'package:flowy_editor/extensions/object_extensions.dart';
|
||||||
@ -26,6 +27,10 @@ mixin FlowySelectionService<T extends StatefulWidget> on State<T> {
|
|||||||
///
|
///
|
||||||
void clearSelection();
|
void clearSelection();
|
||||||
|
|
||||||
|
List<Rect> rects();
|
||||||
|
|
||||||
|
hit(Offset? offset);
|
||||||
|
|
||||||
///
|
///
|
||||||
List<Node> getNodesInSelection(Selection selection);
|
List<Node> getNodesInSelection(Selection selection);
|
||||||
|
|
||||||
@ -108,6 +113,8 @@ class _FlowySelectionState extends State<FlowySelection>
|
|||||||
/// Tap
|
/// Tap
|
||||||
Offset? tapOffset;
|
Offset? tapOffset;
|
||||||
|
|
||||||
|
final List<Rect> _rects = [];
|
||||||
|
|
||||||
EditorState get editorState => widget.editorState;
|
EditorState get editorState => widget.editorState;
|
||||||
|
|
||||||
@override
|
@override
|
||||||
@ -144,8 +151,13 @@ class _FlowySelectionState extends State<FlowySelection>
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
List<Rect> rects() {
|
||||||
|
return _rects;
|
||||||
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
void updateSelection(Selection selection) {
|
void updateSelection(Selection selection) {
|
||||||
|
_rects.clear();
|
||||||
_clearSelection();
|
_clearSelection();
|
||||||
|
|
||||||
// cursor
|
// cursor
|
||||||
@ -245,19 +257,30 @@ class _FlowySelectionState extends State<FlowySelection>
|
|||||||
|
|
||||||
tapOffset = details.globalPosition;
|
tapOffset = details.globalPosition;
|
||||||
|
|
||||||
final nodes = getNodesInRange(tapOffset!);
|
hit(tapOffset);
|
||||||
if (nodes.isNotEmpty) {
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
hit(Offset? offset) {
|
||||||
|
if (offset == null) {
|
||||||
|
editorState.updateCursorSelection(null);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
final nodes = getNodesInRange(offset);
|
||||||
|
if (nodes.isEmpty) {
|
||||||
|
editorState.updateCursorSelection(null);
|
||||||
|
return;
|
||||||
|
}
|
||||||
assert(nodes.length == 1);
|
assert(nodes.length == 1);
|
||||||
final selectable = nodes.first.selectable;
|
final selectable = nodes.first.selectable;
|
||||||
if (selectable != null) {
|
if (selectable == null) {
|
||||||
final position = selectable.getPositionInOffset(tapOffset!);
|
editorState.updateCursorSelection(null);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
final position = selectable.getPositionInOffset(offset);
|
||||||
final selection = Selection.collapsed(position);
|
final selection = Selection.collapsed(position);
|
||||||
editorState.updateCursorSelection(selection);
|
editorState.updateCursorSelection(selection);
|
||||||
}
|
}
|
||||||
} else {
|
|
||||||
editorState.updateCursorSelection(null);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void _onPanStart(DragStartDetails details) {
|
void _onPanStart(DragStartDetails details) {
|
||||||
// clear old state.
|
// clear old state.
|
||||||
@ -353,6 +376,7 @@ class _FlowySelectionState extends State<FlowySelection>
|
|||||||
final rects = selectable.getRectsInSelection(newSelection);
|
final rects = selectable.getRectsInSelection(newSelection);
|
||||||
|
|
||||||
for (final rect in rects) {
|
for (final rect in rects) {
|
||||||
|
_rects.add(_transformRectToGlobal(selectable, rect));
|
||||||
final overlay = OverlayEntry(
|
final overlay = OverlayEntry(
|
||||||
builder: ((context) => SelectionWidget(
|
builder: ((context) => SelectionWidget(
|
||||||
color: widget.selectionColor,
|
color: widget.selectionColor,
|
||||||
@ -367,6 +391,11 @@ class _FlowySelectionState extends State<FlowySelection>
|
|||||||
Overlay.of(context)?.insertAll(_selectionOverlays);
|
Overlay.of(context)?.insertAll(_selectionOverlays);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Rect _transformRectToGlobal(Selectable selectable, Rect r) {
|
||||||
|
final Offset topLeft = selectable.localToGlobal(Offset(r.left, r.top));
|
||||||
|
return Rect.fromLTWH(topLeft.dx, topLeft.dy, r.width, r.height);
|
||||||
|
}
|
||||||
|
|
||||||
void _updateCursor(Position position) {
|
void _updateCursor(Position position) {
|
||||||
final node = editorState.document.root.childAtPath(position.path);
|
final node = editorState.document.root.childAtPath(position.path);
|
||||||
|
|
||||||
@ -380,6 +409,7 @@ class _FlowySelectionState extends State<FlowySelection>
|
|||||||
final selectable = node.selectable;
|
final selectable = node.selectable;
|
||||||
final rect = selectable?.getCursorRectInPosition(position);
|
final rect = selectable?.getCursorRectInPosition(position);
|
||||||
if (rect != null) {
|
if (rect != null) {
|
||||||
|
_rects.add(_transformRectToGlobal(selectable!, rect));
|
||||||
final cursor = OverlayEntry(
|
final cursor = OverlayEntry(
|
||||||
builder: ((context) => CursorWidget(
|
builder: ((context) => CursorWidget(
|
||||||
key: _cursorKey,
|
key: _cursorKey,
|
||||||
@ -390,9 +420,15 @@ class _FlowySelectionState extends State<FlowySelection>
|
|||||||
);
|
);
|
||||||
_cursorOverlays.add(cursor);
|
_cursorOverlays.add(cursor);
|
||||||
Overlay.of(context)?.insertAll(_cursorOverlays);
|
Overlay.of(context)?.insertAll(_cursorOverlays);
|
||||||
|
_forceShowCursor();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
_forceShowCursor() {
|
||||||
|
final currentState = _cursorKey.currentState as CursorWidgetState?;
|
||||||
|
currentState?.show();
|
||||||
|
}
|
||||||
|
|
||||||
List<Node> _selectedNodesInSelection(Node node, Selection selection) {
|
List<Node> _selectedNodesInSelection(Node node, Selection selection) {
|
||||||
List<Node> result = [];
|
List<Node> result = [];
|
||||||
if (node.parent != null) {
|
if (node.parent != null) {
|
||||||
|
Loading…
x
Reference in New Issue
Block a user