From 282efcddca1fe108649de215f46c9679388845a2 Mon Sep 17 00:00:00 2001 From: ma-zea Date: Tue, 10 Oct 2023 17:50:36 +0200 Subject: [PATCH 1/2] IDE-69 Add possibility to drag and drop EndBrick --- .../catroid/ui/dragndrop/BrickListView.kt | 12 +- .../ui/recyclerview/adapter/BrickAdapter.kt | 106 ++++++++++++++- .../brickadapter/EndBrickDragAndDropTest.kt | 121 ++++++++++++++++++ 3 files changed, 235 insertions(+), 4 deletions(-) create mode 100644 catroid/src/test/java/org/catrobat/catroid/test/ui/brickadapter/EndBrickDragAndDropTest.kt diff --git a/catroid/src/main/java/org/catrobat/catroid/ui/dragndrop/BrickListView.kt b/catroid/src/main/java/org/catrobat/catroid/ui/dragndrop/BrickListView.kt index 74f7cd29c52..5754308db78 100644 --- a/catroid/src/main/java/org/catrobat/catroid/ui/dragndrop/BrickListView.kt +++ b/catroid/src/main/java/org/catrobat/catroid/ui/dragndrop/BrickListView.kt @@ -39,6 +39,8 @@ import android.widget.ListAdapter import android.widget.ListView import androidx.annotation.VisibleForTesting import org.catrobat.catroid.content.bricks.Brick +import org.catrobat.catroid.content.bricks.CompositeBrick +import org.catrobat.catroid.content.bricks.EndBrick import java.util.ArrayList private const val SMOOTH_SCROLL_BY = 15 @@ -104,11 +106,15 @@ class BrickListView : ListView { cancelMove() val flatList: MutableList = ArrayList() brickToMove?.addToFlatList(flatList) - if (brickToMove !== flatList[0]) { + if (brickToMove?.parent is CompositeBrick && brickToMove is EndBrick) { + this.brickToMove = brickToMove + flatList.clear() + } else if (brickToMove !== flatList[0]) { return + } else { + this.brickToMove = flatList[0] + flatList.removeAt(0) } - this.brickToMove = flatList[0] - flatList.removeAt(0) upperScrollBound = height / UPPER_SCROLL_BOUND_DIVISOR lowerScrollBound = height / LOWER_SCROLL_BOUND_DIVISOR diff --git a/catroid/src/main/java/org/catrobat/catroid/ui/recyclerview/adapter/BrickAdapter.kt b/catroid/src/main/java/org/catrobat/catroid/ui/recyclerview/adapter/BrickAdapter.kt index 903ef734deb..59da29030bd 100644 --- a/catroid/src/main/java/org/catrobat/catroid/ui/recyclerview/adapter/BrickAdapter.kt +++ b/catroid/src/main/java/org/catrobat/catroid/ui/recyclerview/adapter/BrickAdapter.kt @@ -34,7 +34,9 @@ import androidx.annotation.IntDef import org.catrobat.catroid.content.Script import org.catrobat.catroid.content.Sprite import org.catrobat.catroid.content.bricks.Brick +import org.catrobat.catroid.content.bricks.CompositeBrick import org.catrobat.catroid.content.bricks.EmptyEventBrick +import org.catrobat.catroid.content.bricks.EndBrick import org.catrobat.catroid.content.bricks.FormulaBrick import org.catrobat.catroid.content.bricks.ListSelectorBrick import org.catrobat.catroid.content.bricks.ScriptBrick @@ -365,18 +367,120 @@ class BrickAdapter(private val sprite: Sprite) : if (source !is ScriptBrick && targetPosition == 0) { return false } - if (source.allParts.contains(items[targetPosition])) { + if (source !is EndBrick && source.allParts.contains(items[targetPosition])) { return false } Collections.swap(items, sourcePosition, targetPosition) return true } + private fun getParentBrickInDragAndDropList( + brickAboveTarget: Brick, + enclosureBrick: Brick + ): Pair? { + + if (brickAboveTarget == enclosureBrick) { + return brickAboveTarget to 0 + } + + var brickInEnclosure = brickAboveTarget + while (brickInEnclosure.parent !== null && + brickInEnclosure.parent !in enclosureBrick.allParts && + brickInEnclosure !in enclosureBrick.dragAndDropTargetList + ) { + + brickInEnclosure = brickInEnclosure.parent + } + + if (brickInEnclosure.parent !== enclosureBrick && + brickInEnclosure !in enclosureBrick.dragAndDropTargetList + ) { + return null + } + + return brickInEnclosure to + enclosureBrick.dragAndDropTargetList.indexOf(brickInEnclosure) + 1 + } + + private fun moveEndIntoExtendedSection( + position: Int, + endBrick: Brick, + brickAboveTargetPosition: Brick + ): Boolean { + var tmpParent = brickAboveTargetPosition + val firstPart = endBrick.parent + while (tmpParent.parent != null) { + if (tmpParent is CompositeBrick) { + moveItemTo(position, firstPart) + return true + } + tmpParent = tmpParent.parent + if (tmpParent !is CompositeBrick || tmpParent == firstPart.parent) { + break + } + } + return false + } + + private fun moveEndTo(position: Int, endBrick: Brick, brickAboveTargetPosition: Brick) { + if (endBrick.script !== brickAboveTargetPosition.script) { + return + } + + var startBrick = endBrick.parent + var enclosure = (startBrick as CompositeBrick).nestedBricks + if (startBrick.hasSecondaryList()) { + enclosure = startBrick.secondaryNestedBricks + startBrick = startBrick.allParts[1] + } + + val (parentOfBrickAboveTargetPosition, destinationPosition) = + getParentBrickInDragAndDropList(brickAboveTargetPosition, startBrick) + ?: null to 0 + + val indexStartBrick = getPosition(startBrick) + if (getPosition(brickAboveTargetPosition) + 1 < indexStartBrick) { + return + } + + if (parentOfBrickAboveTargetPosition == null) { + if (moveEndIntoExtendedSection(position, endBrick, brickAboveTargetPosition)) { + return + } + + var (outsideParent, outPosition) = + getParentBrickInDragAndDropList( + brickAboveTargetPosition, + endBrick.parent.parent + ) + ?: return + outsideParent = outsideParent.parent + + val startIndex = + outsideParent.dragAndDropTargetList.indexOf(endBrick.parent) + 1 + for (i in startIndex until outPosition) { + val brick = outsideParent.dragAndDropTargetList.removeAt(startIndex) + brick.parent = startBrick + enclosure.add(brick) + } + } else { + val parentOfCompBrick = endBrick.parent.parent + val positionInList = parentOfCompBrick.dragAndDropTargetList.indexOf(endBrick) + 1 + for (index in (destinationPosition until enclosure.size).withIndex()) { + val brick = enclosure.removeAt(destinationPosition) + brick.parent = parentOfCompBrick + parentOfCompBrick.dragAndDropTargetList.add(positionInList + index.value, brick) + } + } + } + override fun moveItemTo(position: Int, itemToMove: Brick?) { val brickAboveTargetPosition = getBrickAbovePosition(position) if (itemToMove is ScriptBrick) { moveScript(itemToMove, brickAboveTargetPosition) + } else if (itemToMove is EndBrick) { + moveEndTo(position, itemToMove, brickAboveTargetPosition) } else { for (script in scripts) { script.removeBrick(itemToMove) diff --git a/catroid/src/test/java/org/catrobat/catroid/test/ui/brickadapter/EndBrickDragAndDropTest.kt b/catroid/src/test/java/org/catrobat/catroid/test/ui/brickadapter/EndBrickDragAndDropTest.kt new file mode 100644 index 00000000000..229b82fa8fb --- /dev/null +++ b/catroid/src/test/java/org/catrobat/catroid/test/ui/brickadapter/EndBrickDragAndDropTest.kt @@ -0,0 +1,121 @@ +/* + * Catroid: An on-device visual programming system for Android devices + * Copyright (C) 2010-2023 The Catrobat Team + * () + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as + * published by the Free Software Foundation, either version 3 of the + * License, or (at your option) any later version. + * + * An additional term exception under section 7 of the GNU Affero + * General Public License, version 3, is available at + * http://developer.catrobat.org/license_additional_term + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see . + */ + +package org.catrobat.catroid.test.ui.brickadapter + +import org.catrobat.catroid.content.Script +import org.catrobat.catroid.content.Sprite +import org.catrobat.catroid.content.StartScript +import org.catrobat.catroid.content.bricks.ChangeSizeByNBrick +import org.catrobat.catroid.content.bricks.ForeverBrick +import org.catrobat.catroid.content.bricks.IfLogicBeginBrick +import org.catrobat.catroid.content.bricks.SetXBrick +import org.catrobat.catroid.ui.recyclerview.adapter.BrickAdapter +import org.junit.Assert.assertEquals +import org.junit.Assert.assertFalse +import org.junit.Assert.assertTrue +import org.junit.Before +import org.junit.Test +import org.junit.runner.RunWith +import org.junit.runners.JUnit4 +import org.mockito.Mockito.mock +import org.mockito.Mockito.`when` + +@RunWith(JUnit4::class) +class EndBrickDragAndDropTest { + lateinit var adapter: BrickAdapter + lateinit var script: Script + var ifLogicBeginBrick = IfLogicBeginBrick() + var changeSizeByNBrick = ChangeSizeByNBrick() + var setXBrick = SetXBrick() + lateinit var sprite: Sprite + + @Before + fun setUp() { + script = StartScript() + + val scripts: MutableList