-
-
Notifications
You must be signed in to change notification settings - Fork 1.5k
Crearting 'View Trash' and more restore option in Musicblocks #4191
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from 10 commits
c392fae
47d7edb
8ffce6c
9d125e7
b1f1453
8c0b5a3
40ecf20
447916d
a6f5608
3f0ad58
43a63d0
dc96702
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -3320,91 +3320,91 @@ class Activity { | |
| const restoreTrash = (activity) => { | ||
| if (!activity.blocks || !activity.blocks.trashStacks || activity.blocks.trashStacks.length === 0) { | ||
| activity.textMsg( | ||
| _("Nothing in the trash to restore."), | ||
| _("Trash can is empty."), | ||
| 3000 | ||
| ); | ||
| return; | ||
| } | ||
| activity._restoreTrash(); | ||
|
|
||
| if (docById("helpfulWheelDiv").style.display !== "none") { | ||
| docById("helpfulWheelDiv").style.display = "none"; | ||
| activity.__tick(); | ||
| } | ||
| }; | ||
|
|
||
| const restoreTrashPop = (activity) => { | ||
| if (!activity.blocks || !activity.blocks.trashStacks || activity.blocks.trashStacks.length === 0) { | ||
| activity.textMsg( | ||
| _("Trash can is empty."), | ||
| 3000 | ||
| ); | ||
| return; | ||
| } | ||
| for(let i=this.blocks.trashStacks.length - 1;i<this.blocks.trashStacks.length;i++){ | ||
| this._restoreTrashById(this.blocks.trashStacks[i]); | ||
| } | ||
| activity.textMsg( | ||
| _("Item restored from the trash."), | ||
| 3000 | ||
| ); | ||
|
|
||
| if (docById("helpfulWheelDiv").style.display !== "none") { | ||
| docById("helpfulWheelDiv").style.display = "none"; | ||
| activity.__tick(); | ||
| } | ||
| }; | ||
|
|
||
| } | ||
|
|
||
| this._restoreTrash = () => { | ||
| this._restoreTrashById = (blockId) => { | ||
| const blockIndex = this.blocks.trashStacks.indexOf(blockId); | ||
| if (blockIndex === -1) return; // Block not found in trash | ||
|
|
||
| this.blocks.trashStacks.splice(blockIndex, 1); // Remove from trash | ||
|
|
||
| for (const name in this.palettes.dict) { | ||
| this.palettes.dict[name].hideMenu(true); | ||
| } | ||
|
|
||
| this.blocks.activeBlock = null; | ||
| this.refreshCanvas(); | ||
|
|
||
| const dx = 0; | ||
| const dy = -this.cellSize * 3; // Reposition | ||
|
|
||
| if (this.blocks.trashStacks.length === 0) { | ||
| return; | ||
| } | ||
|
|
||
| const thisBlock = this.blocks.trashStacks.pop(); | ||
|
|
||
| // Restore drag group in trash | ||
| this.blocks.findDragGroup(thisBlock); | ||
|
|
||
| // Restore drag group | ||
| this.blocks.findDragGroup(blockId); | ||
| for (let b = 0; b < this.blocks.dragGroup.length; b++) { | ||
| const blk = this.blocks.dragGroup[b]; | ||
| this.blocks.blockList[blk].trash = false; | ||
| this.blocks.moveBlockRelative(blk, dx, dy); | ||
| this.blocks.blockList[blk].show(); | ||
| } | ||
|
|
||
| this.blocks.raiseStackToTop(thisBlock); | ||
|
|
||
| if ( | ||
| this.blocks.blockList[thisBlock].name === "start" || | ||
| this.blocks.blockList[thisBlock].name === "drum" | ||
| ) { | ||
| const turtle = this.blocks.blockList[thisBlock].value; | ||
|
|
||
| this.blocks.raiseStackToTop(blockId); | ||
| const restoredBlock = this.blocks.blockList[blockId]; | ||
|
|
||
| if (restoredBlock.name === 'start' || restoredBlock.name === 'drum') { | ||
| const turtle = restoredBlock.value; | ||
| this.turtles.turtleList[turtle].inTrash = false; | ||
| this.turtles.turtleList[turtle].container.visible = true; | ||
| } else if (this.blocks.blockList[thisBlock].name === "action") { | ||
| // We need to add a palette entry for this action. | ||
| // But first we need to ensure we have a unqiue name, | ||
| // as the name could have been taken in the interim. | ||
| const actionArg = this.blocks.blockList[ | ||
| this.blocks.blockList[thisBlock].connections[1] | ||
| ]; | ||
| } else if (restoredBlock.name === 'action') { | ||
| const actionArg = this.blocks.blockList[restoredBlock.connections[1]]; | ||
| if (actionArg !== null) { | ||
| let label; | ||
| const oldName = actionArg.value; | ||
| // Mark the action block as still being in the | ||
| // trash so that its name won't be considered when | ||
| // looking for a unique name. | ||
| this.blocks.blockList[thisBlock].trash = true; | ||
| restoredBlock.trash = true; | ||
| const uniqueName = this.blocks.findUniqueActionName(oldName); | ||
| this.blocks.blockList[thisBlock].trash = false; | ||
| restoredBlock.trash = false; | ||
|
|
||
| if (uniqueName !== actionArg) { | ||
| actionArg.value = uniqueName; | ||
|
|
||
| label = actionArg.value.toString(); | ||
| if (label.length > 8) { | ||
| label = label.substr(0, 7) + "..."; | ||
| } | ||
| label = uniqueName.length > 8 ? uniqueName.substr(0, 7) + '...' : uniqueName; | ||
| actionArg.text.text = label; | ||
|
|
||
| if (actionArg.label !== null) { | ||
| actionArg.label.value = uniqueName; | ||
| } | ||
|
|
||
| actionArg.container.updateCache(); | ||
|
|
||
| // Check the drag group to ensure any do blocks are updated (in case of recursion). | ||
| for (let b = 0; b < this.blocks.dragGroup.length; b++) { | ||
| const me = this.blocks.blockList[this.blocks.dragGroup[b]]; | ||
| if ( | ||
|
|
@@ -3415,11 +3415,7 @@ class Activity { | |
| ) { | ||
| me.privateData = uniqueName; | ||
| me.value = uniqueName; | ||
|
|
||
| label = me.value.toString(); | ||
| if (label.length > 8) { | ||
| label = label.substr(0, 7) + "..."; | ||
| } | ||
| label = uniqueName.length > 8 ? uniqueName.substr(0, 7) + '...' : uniqueName; | ||
| me.text.text = label; | ||
| me.overrideName = label; | ||
| me.regenerateArtwork(); | ||
|
|
@@ -3429,21 +3425,112 @@ class Activity { | |
| } | ||
| } | ||
| } | ||
|
|
||
| activity.textMsg( | ||
| _("Item restored from the trash."), | ||
| 3000 | ||
| ); | ||
|
|
||
| this.refreshCanvas(); | ||
| }; | ||
| }; | ||
|
|
||
| // Add event listener for trash icon click | ||
| document.getElementById('restoreIcon').addEventListener('click', () => { | ||
| this._renderTrashView(); | ||
| }); | ||
|
|
||
| // function to hide trashView from canvas | ||
| function handleClickOutsideTrashView(trashView) { | ||
| let firstClick = true; | ||
| document.addEventListener('click', (event) => { | ||
| if (firstClick) { | ||
| firstClick = false; | ||
| return; | ||
| } | ||
| if (!trashView.contains(event.target) && event.target !== trashView) { | ||
| trashView.style.display = 'none'; | ||
| } | ||
| }); | ||
| } | ||
|
|
||
| this.handleKeyDown = (event) => { | ||
|
|
||
| if (event.ctrlKey && event.key === "z") { | ||
| this._restoreTrash(activity); | ||
| activity.__tick(); | ||
| event.preventDefault(); | ||
| this._renderTrashView = () => { | ||
| if (!activity.blocks || !activity.blocks.trashStacks || activity.blocks.trashStacks.length === 0) { | ||
| return; | ||
| } | ||
| }; | ||
|
|
||
| // Attach keydown event listener to document | ||
| document.addEventListener("keydown", this.handleKeyDown); | ||
| const trashList = document.getElementById('trashList'); | ||
| const trashView = document.createElement('div'); | ||
| trashView.id = 'trashView'; | ||
| trashView.classList.add('trash-view'); | ||
|
|
||
| // Sticky icons | ||
| const buttonContainer = document.createElement('div'); | ||
| buttonContainer.classList.add('button-container'); | ||
|
|
||
| const restoreLastIcon = document.createElement('a'); | ||
| restoreLastIcon.id = 'restoreLastIcon'; | ||
| restoreLastIcon.classList.add('restore-last-icon'); | ||
| restoreLastIcon.innerHTML = '<i class="material-icons md-48">restore_from_trash</i>'; | ||
| restoreLastIcon.addEventListener('click', () => { | ||
| for(let i=this.blocks.trashStacks.length - 1;i<this.blocks.trashStacks.length;i++){ | ||
|
||
| this._restoreTrashById(this.blocks.trashStacks[i]); | ||
| } | ||
| trashView.classList.add('hidden'); | ||
| }); | ||
|
|
||
| const restoreAllIcon = document.createElement('a'); | ||
| restoreAllIcon.id = 'restoreAllIcon'; | ||
| restoreAllIcon.classList.add('restore-all-icon'); | ||
| restoreAllIcon.innerHTML = '<i class="material-icons md-48">delete_sweep</i>'; | ||
| restoreAllIcon.addEventListener('click', () => { | ||
| while (this.blocks.trashStacks.length > 0) { | ||
| this._restoreTrashById(this.blocks.trashStacks[0]); | ||
| } | ||
| trashView.classList.add('hidden'); | ||
| }); | ||
| restoreLastIcon.setAttribute("title", _("Restore last item")); | ||
| restoreAllIcon.setAttribute("title", _("Restore all items")); | ||
|
|
||
| buttonContainer.appendChild(restoreLastIcon); | ||
| buttonContainer.appendChild(restoreAllIcon); | ||
| trashView.appendChild(buttonContainer); | ||
|
|
||
| // Render trash items | ||
| this.blocks.trashStacks.forEach((blockId) => { | ||
| const block = this.blocks.blockList[blockId]; | ||
| const listItem = document.createElement('div'); | ||
| listItem.classList.add('trash-item'); | ||
|
|
||
| const svgData = block.artwork; | ||
| const encodedData = 'data:image/svg+xml;utf8,' + encodeURIComponent(svgData); | ||
|
|
||
| const img = document.createElement('img'); | ||
| img.src = encodedData; | ||
| img.alt = 'Block Icon'; | ||
| img.classList.add('trash-item-icon'); | ||
|
|
||
| const textNode = document.createTextNode(block.name); | ||
|
|
||
| listItem.appendChild(img); | ||
| listItem.appendChild(textNode); | ||
| listItem.dataset.blockId = blockId; | ||
|
|
||
| listItem.addEventListener('mouseover', () => listItem.classList.add('hover')); | ||
| listItem.addEventListener('mouseout', () => listItem.classList.remove('hover')); | ||
| listItem.addEventListener('click', () => { | ||
| this._restoreTrashById(blockId); | ||
| trashView.classList.add('hidden'); | ||
| }); | ||
| handleClickOutsideTrashView(trashView); | ||
|
|
||
| trashView.appendChild(listItem); | ||
| }); | ||
|
|
||
| const existingView = document.getElementById('trashView'); | ||
| if (existingView) { | ||
| trashList.replaceChild(trashView, existingView); | ||
| } else { | ||
| trashList.appendChild(trashView); | ||
| } | ||
| }; | ||
|
|
||
| /* | ||
| * Open aux menu | ||
|
|
@@ -5814,7 +5901,7 @@ class Activity { | |
| this.helpfulWheelItems.push({label: "Increase block size", icon: "imgsrc:data:image/svg+xml;base64," + window.btoa(base64Encode(BIGGERBUTTON)), display: true, fn: doLargerBlocks}); | ||
|
|
||
| if (!this.helpfulWheelItems.find(ele => ele.label === "Restore")) | ||
| this.helpfulWheelItems.push({label: "Restore", icon: "imgsrc:header-icons/restore-from-trash.svg", display: true, fn: restoreTrash}); | ||
| this.helpfulWheelItems.push({label: "Restore", icon: "imgsrc:header-icons/restore-from-trash.svg", display: true, fn: restoreTrashPop}); | ||
|
|
||
| if (!this.helpfulWheelItems.find(ele => ele.label === "Turtle Wrap Off")) | ||
| this.helpfulWheelItems.push({label: "Turtle Wrap Off", icon: "imgsrc:header-icons/wrap-text.svg", display: true, fn: this.toolbar.changeWrap}); | ||
|
|
||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Maybe the background for this container should be the same blue we use for toolbars.
Maybe the buttons should be icons with hover help text instead of labels. (We'll need to decide on what icons.)