From 41f605f843b2bb0cf59830e12a944d0016082835 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jakub=20Zl=C3=A1mal?= Date: Sun, 31 Dec 2023 08:47:32 +0100 Subject: [PATCH] Implemented moving selection across pages. Undo/redo works. In Select added pageIndexStart holding original page of selection. In EditorHistory added pageIndexStart - important only for move Solving issue #932 --- lib/data/editor/editor_history.dart | 6 ++ lib/data/tools/select.dart | 6 ++ lib/pages/editor/editor.dart | 97 ++++++++++++++++++++++------- 3 files changed, 88 insertions(+), 21 deletions(-) diff --git a/lib/data/editor/editor_history.dart b/lib/data/editor/editor_history.dart index f67cb5a92..3d39d87bd 100644 --- a/lib/data/editor/editor_history.dart +++ b/lib/data/editor/editor_history.dart @@ -122,8 +122,11 @@ class EditorHistoryItem { this.page, this.quillChange, this.colorChange, + this.pageIndexStart, // original page of selection before it was moved to another page }) : assert(type != EditorHistoryItemType.move || offset != null, 'Offset must be provided for move'), + assert(type != EditorHistoryItemType.move || pageIndexStart != null, + 'PageIndexStart must be provided for move'), assert(type != EditorHistoryItemType.deletePage || page != null, 'Page must be provided for deletePage'), assert(type != EditorHistoryItemType.insertPage || page != null, @@ -141,6 +144,7 @@ class EditorHistoryItem { final EditorHistoryItemType type; final int pageIndex; + final int? pageIndexStart; // original page of selected items before moved to another one final List strokes; final List images; final Rect? offset; @@ -151,6 +155,7 @@ class EditorHistoryItem { EditorHistoryItem copyWith({ EditorHistoryItemType? type, int? pageIndex, + int? pageIndexStart, List? strokes, List? images, Rect? offset, @@ -161,6 +166,7 @@ class EditorHistoryItem { return EditorHistoryItem( type: type ?? this.type, pageIndex: pageIndex ?? this.pageIndex, + pageIndexStart: pageIndexStart ?? this.pageIndexStart, strokes: strokes ?? this.strokes, images: images ?? this.images, offset: offset ?? this.offset, diff --git a/lib/data/tools/select.dart b/lib/data/tools/select.dart index 311777074..6ca44822b 100644 --- a/lib/data/tools/select.dart +++ b/lib/data/tools/select.dart @@ -18,6 +18,7 @@ class Select extends Tool { strokes: const [], images: const [], path: Path(), + pageIndexStart: -1, // page index when selection was created ); bool doneSelecting = false; @@ -57,6 +58,7 @@ class Select extends Tool { strokes: [], images: [], path: Path(), + pageIndexStart: pageIndex, // page index when selection was created ); selectResult.path.moveTo(position.dx, position.dy); onDragUpdate(position); @@ -127,12 +129,14 @@ class SelectResult { final List strokes; final List images; Path path; + int pageIndexStart; SelectResult({ required this.pageIndex, required this.strokes, required this.images, required this.path, + required this.pageIndexStart, }); bool get isEmpty { @@ -144,12 +148,14 @@ class SelectResult { List? strokes, List? images, Path? path, + int? pageIndexStart, }) { return SelectResult( pageIndex: pageIndex ?? this.pageIndex, strokes: strokes ?? this.strokes, images: images ?? this.images, path: path ?? this.path, + pageIndexStart: pageIndexStart ?? this.pageIndexStart, ); } } diff --git a/lib/pages/editor/editor.dart b/lib/pages/editor/editor.dart index 3fd99d1e1..5dba3294d 100644 --- a/lib/pages/editor/editor.dart +++ b/lib/pages/editor/editor.dart @@ -411,11 +411,17 @@ class EditorState extends State { } case EditorHistoryItemType.move: + // all movement of selection was on one page for (Stroke stroke in item.strokes) { stroke.shift(Offset( -item.offset!.left, -item.offset!.top, )); + if (item.pageIndex!=item.pageIndexStart) { + // stroke must be on original page when selection movements started + int pageIndexStart = item.pageIndexStart ?? -1; + moveStrokeToPage(stroke, item.pageIndex, pageIndexStart); + } } Select select = Select.currentSelect; if (select.doneSelecting) { @@ -423,6 +429,10 @@ class EditorState extends State { -item.offset!.left, -item.offset!.top, )); + if (item.pageIndex!=item.pageIndexStart) { + int pageIndexStart = item.pageIndexStart ?? -1; + select.selectResult.pageIndex=pageIndexStart; + } } for (EditorImage image in item.images) { image.dstRect = Rect.fromLTRB( @@ -431,6 +441,11 @@ class EditorState extends State { image.dstRect.right - item.offset!.right, image.dstRect.bottom - item.offset!.bottom, ); + if (item.pageIndex!=item.pageIndexStart) { + // image must be on original page when selection movements started + int pageIndexStart = item.pageIndexStart ?? 0; + moveImageToPage(image, item.pageIndex, pageIndexStart); + } } case EditorHistoryItemType.quillChange: @@ -469,6 +484,8 @@ class EditorState extends State { undo(item.copyWith(type: EditorHistoryItemType.deletePage)); case EditorHistoryItemType.move: undo(item.copyWith( + pageIndex: item.pageIndexStart, // exchange page and Start page + pageIndexStart: item.pageIndex, offset: Rect.fromLTRB( -item.offset!.left, -item.offset!.top, @@ -638,7 +655,9 @@ class EditorState extends State { } // shift also selection path select.selectResult.path = select.selectResult.path.shift(offset); + if (pageOffset!=0) { + offset = position - previousPosition; // and put real offset back // before page redraw we need to move selected entities to another page // this page will be redrawn later //log.info('Moving selected item to new page and deleting them from original page'); @@ -666,8 +685,9 @@ class EditorState extends State { position = pageNew.renderBox!.globalToLocal(details.focalPoint); double rectBottom=pageNew.size.height; //log.info('New page rect $rectTop to $rectBottom. Position on new page is $cursorPosition'); - offset=Offset(offset.dx,offset.dy-pageOffset*(rectBottom+distOffset)); // put offset to original page - setState(() {}); // force update of builder so movement of selection to another page is taken into account + // recalculate the offset as if the selection were always moving to one page. Important for undo/redo + offset=Offset(offset.dx,offset.dy-pageOffset*(rectBottom+distOffset)); + // setState(() {}); // force update of builder so movement of selection to another page is taken into account pageNew.redrawStrokes(); // and finally redraw new page } @@ -675,6 +695,47 @@ class EditorState extends State { moveOffset += offset; // this value is keeped due to Undo/redo } + bool moveStrokeToPage(Stroke stroke,int pageIndexOrig,int pageIndexDest){ + // move stroke from page pageIndexOrig to pageIndexDest + // setState must be called before use to track changes + if (pageIndexOrig==pageIndexDest || pageIndexOrig==-1 || pageIndexDest==-1){ + return false; // no page change + } + if (pageIndexOrig<0 || pageIndexOrig>coreInfo.pages.length-1){ + return false; + } + if (pageIndexDest<0 || pageIndexDest>coreInfo.pages.length-1){ + return false; + } + final pageOrig = coreInfo.pages[pageIndexOrig]; + final pageDest = coreInfo.pages[pageIndexDest]; + // remove from original page + pageOrig.strokes.remove(stroke); + // add to new page + pageDest.strokes.add(stroke); + return true; + } + bool moveImageToPage(EditorImage image,int pageIndexOrig,int pageIndexDest){ + // move image from page pageIndexOrig to pageIndexDest + // setState must be called before use to track changes + if (pageIndexOrig==pageIndexDest || pageIndexOrig==-1 || pageIndexDest==-1){ + return false; // no page change + } + if (pageIndexOrig<0 || pageIndexOrig>coreInfo.pages.length-1){ + return false; + } + if (pageIndexDest<0 || pageIndexDest>coreInfo.pages.length-1){ + return false; + } + final pageOrig = coreInfo.pages[pageIndexOrig]; + final pageDest = coreInfo.pages[pageIndexDest]; + // remove from original page + pageOrig.images.remove(image); + // add to new page + pageDest.images.add(image); + return true; + } + void selectionOffsetPage(int pageOffset){ // selected items should be moved to another page Select select = currentTool as Select; @@ -687,27 +748,20 @@ class EditorState extends State { if (newPage<0 || newPage>coreInfo.pages.length-1){ return; } - final pageOld = coreInfo.pages[oldPage]; - final pageNew = coreInfo.pages[newPage]; final strokes = select.selectResult.strokes; final images = select.selectResult.images; - // remove from original page - for (Stroke stroke in strokes) { - pageOld.strokes.remove(stroke); - } - for (EditorImage image in images) { - pageOld.images.remove(image); - } - // add to new page - for (Stroke stroke in strokes) { - pageNew.strokes.add(stroke); - } - for (EditorImage image in images) { - pageNew.images.add(image); - } - // move selection to new page - select.selectResult.pageIndex+=pageOffset; + setState(() { + // remove from original page + for (Stroke stroke in strokes) { + moveStrokeToPage(stroke, oldPage, newPage); + } + for (EditorImage image in images) { + moveImageToPage(image, oldPage, newPage); + } + // move selection to new page + select.selectResult.pageIndex += pageOffset; + }); } void onDrawEnd(ScaleEndDetails details) { @@ -745,7 +799,8 @@ class EditorState extends State { if (select.doneSelecting) { history.recordChange(EditorHistoryItem( type: EditorHistoryItemType.move, - pageIndex: dragPageIndex!, + pageIndexStart: select.selectResult.pageIndexStart, // use page index of page where entities were aat start of movement + pageIndex: select.selectResult.pageIndex, // use page index of page where entities are now strokes: select.selectResult.strokes, images: select.selectResult.images, offset: Rect.fromLTRB(