From 17f8fd8f666e3b3013908caf90cd81eb74f303a9 Mon Sep 17 00:00:00 2001 From: Vlad Stepura Date: Wed, 26 Aug 2020 14:52:08 +0300 Subject: [PATCH 01/23] Created a grid view for boards preview --- src/css/mainwindow.css | 41 ++++++ src/css/storyboarder-sketch-pane.css | 1 - src/js/window/main-window.js | 180 +++++++++++++++++++++++++++ 3 files changed, 221 insertions(+), 1 deletion(-) diff --git a/src/css/mainwindow.css b/src/css/mainwindow.css index 6be781e6d1..5eca5f281e 100644 --- a/src/css/mainwindow.css +++ b/src/css/mainwindow.css @@ -1004,3 +1004,44 @@ input[type=range]:focus::-webkit-slider-runnable-track { -webkit-user-drag: none; user-drag: none; } + + +.grid-view { + /*transform: scale(0.0,0.0) translate(0,0);*/ + /*transform-origin: 0 0;*/ + position: relative; + /* background: white; */ + /*border-radius: 13px;*/ + box-shadow: 0 2px 15px rgba(0, 0, 0, 0.3); + flex-shrink: 0; + width: 100%; + height: 100%; + padding: 10px; + overflow: auto; + grid-column-gap: 50px; + grid-row-gap: 50px; + display: grid; + grid-template-columns: auto auto auto; +} + +.thumbnail { + flex: 1; + background: #444; + padding: 5px; + margin-right: 0px; + font-size: 10px; + color: #888; + font-weight: 100; + box-shadow: 0 1px 1px rgba(0,0,0,0.2); + transition: all 0s ease-in-out; +} + +.thumbnail.selected { + background: #8d89cf; + color: #000; + top: 0px; + position: relative; + transition: all 0s ease-out; + box-shadow: none; + z-index: 999; +} \ No newline at end of file diff --git a/src/css/storyboarder-sketch-pane.css b/src/css/storyboarder-sketch-pane.css index d043c1fba8..42229c79c3 100644 --- a/src/css/storyboarder-sketch-pane.css +++ b/src/css/storyboarder-sketch-pane.css @@ -7,7 +7,6 @@ #storyboarder-sketch-pane { flex: 1; - display: flex; position: relative; width: 100%; height: 100%; diff --git a/src/js/window/main-window.js b/src/js/window/main-window.js index 0f99801376..9a092eda65 100644 --- a/src/js/window/main-window.js +++ b/src/js/window/main-window.js @@ -6522,16 +6522,186 @@ const updateSceneFromScript = async () => { renderScript() } +const setSketchPaneVisibility = (isVisible) => { + let storyboarderSketchPane = document.querySelector("#storyboarder-sketch-pane") + let container = storyboarderSketchPane.getElementsByClassName("container")[0] + if(isVisible) { + container.style["visibility"] = "visible"; + container.style["position"] = "relative"; + } else { + container.style["position"] = "absolute"; + container.style["visibility"] = "hidden"; + } +} + + +const renderGridView = () => { + setSketchPaneVisibility(false) + let gridContainer = document.createElement("div") + let hasShots = boardData.boards.find(board => board.newShot) != null + let html = [] + let i = 0 + for (let board of boardData.boards) { + html.push('
') + let imageFilename = path.join(boardPath, 'images', board.url.replace('.png', '-thumbnail.png')) + try { + if (fs.existsSync(imageFilename)) { + html.push('
') + let src = imageFilename + '?' + getEtag(path.join(boardPath, 'images', boardModel.boardFilenameForThumbnail(board))) + html.push('') + html.push('
') + } else { + // blank image + html.push(`') + } + } catch (err) { + log.error(err) + } + html.push('
') + html.push('
' + board.shot + '
') + if (board.audio && board.audio.filename.length) { + html.push(` +
+ + + +
+ `) + } + html.push('
') + if (board.dialogue) { + html.push(board.dialogue) + } + html.push('
') + if (board.duration) { + html.push(util.msToTime(board.duration)) + } else { + html.push(util.msToTime(boardData.defaultBoardTiming)) + } + html.push('
') + html.push('
') + html.push('
') + i++ + } + gridContainer.innerHTML = html.join('') + gridContainer.className = "grid-view" + let storyboarderSketchPane = document.querySelector("#storyboarder-sketch-pane") + storyboarderSketchPane.appendChild(gridContainer) + + + let thumbnails = gridContainer.querySelectorAll('.thumbnail') + console.log(thumbnails) + for (let j = 0; j < thumbnails.length; j++) { + let thumb = thumbnails[j] + console.log(thumb) + thumb.addEventListener('pointerenter', (e) => { + if (!isEditMode && selections.size <= 1 && e.currentTarget.dataset.thumbnail === currentBoard) { + contextMenu.attachTo(e.currentTarget) + } + }) + thumb.addEventListener('pointerleave', (e) => { + if (!contextMenu.hasChild(e.relatedTarget)) { + contextMenu.remove() + } + }) + thumb.addEventListener('pointermove', (e) => { + if (!isEditMode && selections.size <= 1 && e.currentTarget.dataset.thumbnail === currentBoard) { + contextMenu.attachTo(e.currentTarget) + } + }) + thumb.addEventListener('pointerdown', (e) => { + log.info('DOWN') + if (!isEditMode && selections.size <= 1) contextMenu.attachTo(e.currentTarget) + + // always track cursor position + updateThumbnailCursor(e.clientX, e.clientY) + + if (e.button === 0) { + editModeTimer = setTimeout(enableEditMode, enableEditModeDelay) + } else { + enableEditMode() + } + + let index = Number(e.currentTarget.dataset.thumbnail) + if (selections.has(index)) { + // ignore + } else if (isCommandPressed('workspace:thumbnails:select-multiple-modifier')) { + if (selections.size === 0 && !util.isUndefined(currentBoard)) { + // use currentBoard as starting point + selections.add(currentBoard) + } + + // add to selections + let min = Math.min(...selections, index) + let max = Math.max(...selections, index) + selections = new Set(util.range(min, max)) + + renderThumbnailDrawerSelections() + } else if (currentBoard !== index) { + // go to board by index + + // reset selections + selections.clear() + console.log(e) + log.info(`Current board ${currentBoard} and index ${index}`) + saveImageFile().then(() => { + currentBoard = index + renderThumbnailDrawerSelections() + gotoBoard(currentBoard) + }) + } + }, true, true) + } +} +const cleanUpGridView = () => { + let storyboarderSketchPane = document.querySelector("#storyboarder-sketch-pane") + let gridView = storyboarderSketchPane.querySelector(".grid-view") + if(!gridView) return + storyboarderSketchPane.removeChild(gridView) +} +// TODO(): Find a better way to switch modes +let isGridViewMode = false const TimelineModeControlView = ({ mode = 'sequence', show = false }) => { let style = { display: show ? 'flex' : 'none' } const onBoardsSelect = () => { + isGridViewMode = false shouldRenderThumbnailDrawer = false + setSketchPaneVisibility(true) renderThumbnailDrawer() + cleanUpGridView() } const onTimelineSelect = () => { + isGridViewMode = false + shouldRenderThumbnailDrawer = true + setSketchPaneVisibility(true) + renderThumbnailDrawer() + cleanUpGridView() + } + const onGridViewSelect = () => { shouldRenderThumbnailDrawer = true + isGridViewMode = true renderThumbnailDrawer() + renderGridView() } return h( @@ -6554,6 +6724,16 @@ const TimelineModeControlView = ({ mode = 'sequence', show = false }) => { ['use', { xlinkHref: './img/symbol-defs.svg#timeline-timeline' }] ], ['span', 'Timeline'] + ], + ['div.spacer'], + ['div.btn', { + className: isGridViewMode ? 'selected' : null, + onPointerUp: onGridViewSelect + }, + ['svg', { className: 'icon' }, + ['use', { xlinkHref: './img/symbol-defs.svg#timeline-timeline' }] + ], + ['span', 'Grid View'] ] ] ) From edd14c6b364fa2b7dadbcc81b317ca2a922b68e3 Mon Sep 17 00:00:00 2001 From: Vlad Stepura Date: Thu, 27 Aug 2020 10:49:29 +0300 Subject: [PATCH 02/23] Fixed initial board selection --- src/js/window/main-window.js | 28 +++++++--------------------- 1 file changed, 7 insertions(+), 21 deletions(-) diff --git a/src/js/window/main-window.js b/src/js/window/main-window.js index 9a092eda65..8847dbba4f 100644 --- a/src/js/window/main-window.js +++ b/src/js/window/main-window.js @@ -3937,6 +3937,7 @@ const renderSceneTimeline = () => { let renderThumbnailDrawer = () => { updateSceneTiming() + isGridViewMode && renderGridView() // reflect the current view cycleViewMode(0) @@ -4009,7 +4010,8 @@ let renderThumbnailDrawer = () => { html.push('') i++ } - document.querySelector('#thumbnail-drawer').innerHTML = html.join('') + let thumbnailDrawer = document.querySelector('#thumbnail-drawer') + thumbnailDrawer.innerHTML = html.join('') renderThumbnailButtons() @@ -4072,7 +4074,7 @@ let renderThumbnailDrawer = () => { }) } - let thumbnails = document.querySelectorAll('.thumbnail') + let thumbnails = thumbnailDrawer.querySelectorAll('.thumbnail') for (var thumb of thumbnails) { thumb.addEventListener('pointerenter', (e) => { if (!isEditMode && selections.size <= 1 && e.target.dataset.thumbnail === currentBoard) { @@ -6536,29 +6538,13 @@ const setSketchPaneVisibility = (isVisible) => { const renderGridView = () => { + cleanUpGridView() setSketchPaneVisibility(false) let gridContainer = document.createElement("div") - let hasShots = boardData.boards.find(board => board.newShot) != null let html = [] let i = 0 for (let board of boardData.boards) { html.push('
') @@ -6609,10 +6595,8 @@ const renderGridView = () => { let thumbnails = gridContainer.querySelectorAll('.thumbnail') - console.log(thumbnails) for (let j = 0; j < thumbnails.length; j++) { let thumb = thumbnails[j] - console.log(thumb) thumb.addEventListener('pointerenter', (e) => { if (!isEditMode && selections.size <= 1 && e.currentTarget.dataset.thumbnail === currentBoard) { contextMenu.attachTo(e.currentTarget) @@ -6671,6 +6655,8 @@ const renderGridView = () => { } }, true, true) } + + renderThumbnailDrawerSelections() } const cleanUpGridView = () => { let storyboarderSketchPane = document.querySelector("#storyboarder-sketch-pane") From c73dd3a5bfa8f05b6167ce62d0a2aeaad58d7209 Mon Sep 17 00:00:00 2001 From: Vlad Stepura Date: Thu, 27 Aug 2020 16:17:43 +0300 Subject: [PATCH 03/23] Added element arrow highlight --- src/css/mainwindow.css | 23 ++++++++ src/js/window/main-window.js | 104 +++++++++++++++++++++++++++++++++-- src/main-window.html | 1 + 3 files changed, 124 insertions(+), 4 deletions(-) diff --git a/src/css/mainwindow.css b/src/css/mainwindow.css index 5eca5f281e..2bf40b3059 100644 --- a/src/css/mainwindow.css +++ b/src/css/mainwindow.css @@ -203,6 +203,29 @@ body::-webkit-scrollbar { animation-name: bounce; } +#grid-view-cursor { + position: relative; + top: 0; + left: 0; + margin-top: 7px; + + width: 0; + height: 0; + border-left: 8px solid transparent; + border-right: 8px solid transparent; + border-top: 10px solid #999; + transition: left .1s ease-in; + animation-delay: 0s; + animation-duration: 0.5s; + animation-timing-function: ease-in; + animation-iteration-count: infinite; + animation-name: bounce; +} +#grid-cursor-container { + position: absolute; + z-index: 9999; +} + @keyframes bounce { 0% { top: 5px; diff --git a/src/js/window/main-window.js b/src/js/window/main-window.js index 8847dbba4f..ad53a366b2 100644 --- a/src/js/window/main-window.js +++ b/src/js/window/main-window.js @@ -1083,6 +1083,7 @@ const loadBoardUI = async () => { window.addEventListener('pointerup', (e)=>{ if (dragMode) { + console.log("disable dragmode") disableDragMode() preventDragMode = false } @@ -4449,6 +4450,8 @@ let updateDrag = () => { setDragTarget(lastPointer.x) updateThumbnailCursor(lastPointer.x, lastPointer.y) renderThumbnailCursor() + isGridViewMode && updateGridViewCursor(lastPointer.x, lastPointer.y) + isGridViewMode && renderGridViewCursor() } } @@ -6017,8 +6020,11 @@ let enableEditMode = () => { if (!isEditMode && selections.size) { isEditMode = true thumbnailCursor.visible = true + gridViewCursor.visible = true renderThumbnailCursor() + isGridViewMode && renderGridViewCursor() renderThumbnailDrawerSelections() + isGridViewMode && renderGridViewCursor() contextMenu.remove() sfx.positive() sfx.playEffect('on') @@ -6032,7 +6038,9 @@ let disableEditMode = () => { sfx.negative() isEditMode = false thumbnailCursor.visible = false + gridViewCursor.visible = false renderThumbnailCursor() + isGridViewMode && renderGridViewCursor() renderThumbnailDrawerSelections() } } @@ -6040,7 +6048,6 @@ let disableEditMode = () => { let thumbnailFromPoint = (x, y, offset) => { if (!offset) { offset = 0 } let el = document.elementFromPoint(x-offset, y) - if (!el || !el.classList.contains('thumbnail')) return null // if part of a multi-selection, base from right-most element @@ -6536,11 +6543,98 @@ const setSketchPaneVisibility = (isVisible) => { } } +//#region Grid view +let gridViewFromPoint = (x, y, offset) => { + if (!offset) { offset = 0 } + let el = document.elementFromPoint(x-offset, y) + if (!el) return null + + // if part of a multi-selection, base from right-most element + if (selections.has(Number(el.dataset.thumbnail))) { + // base from the right-most thumbnail in the selection + let rightMost = Math.max(...selections) + let rightMostEl = document.querySelector('#thumbnail-drawer div[data-thumbnail="' + rightMost + '"]') + el = rightMostEl + } + + return el +} + +let gridViewCursor = { + visible: false, + x: 0, + el: null +} +let updateGridViewCursor = (x, y) => { + // let shouldRenderThumbnailDrawer = false + if (!shouldRenderThumbnailDrawer) return + +/* if (isBeforeFirstThumbnail(x, y)) { + thumbnailCursor.x = 0 + thumbnailCursor.el = null + return + } */ + + let el = gridViewFromPoint(x, y) + let offset = 0 + if (el) { + offset = el.getBoundingClientRect().width + el = gridViewFromPoint(x, y, offset/2) + } + + if (el) gridViewCursor.el = el // only update if found + if (!el) return + + // store a reference to the nearest thumbnail + gridViewCursor.el = el + + let elementOffsetX = el.getBoundingClientRect().right + + let elementOffsetY = el.getBoundingClientRect().top + + // is this an end shot? + if (el.classList.contains('endShot')) { + elementOffsetX += 5 + } + + let arrowOffsetX = 8 + let arrowOffsetY = -8 + console.log("gridViewCursor", gridViewCursor) + gridViewCursor.x = elementOffsetX + arrowOffsetX + + gridViewCursor.y = elementOffsetY + arrowOffsetY +} + +let renderGridViewCursor = () => { + let el = document.querySelector('#grid-cursor-container') + if (el) { // shouldRenderThumbnailDrawer + if (gridViewCursor.visible) { + el.style.display = '' + el.style.left = gridViewCursor.x + 'px' + el.style.top = gridViewCursor.y + 'px' + } else { + el.style.display = 'none' + el.style.left = '0px' + } + } +} +const gridDrag = (e)=>{ + if (e.pointerType == 'pen' || e.pointerType == 'mouse') { + dragTarget = document.querySelector('.grid-view') + dragTarget.style.overflow = 'hidden' + dragTarget.style.scrollBehavior = 'unset' + dragMode = true + dragPoint = [e.pageX, e.pageY] + scrollPoint = [dragTarget.scrollLeft, dragTarget.scrollTop] + periodicDragUpdate() + } +} const renderGridView = () => { cleanUpGridView() setSketchPaneVisibility(false) let gridContainer = document.createElement("div") + let html = [] let i = 0 for (let board of boardData.boards) { @@ -6590,6 +6684,7 @@ const renderGridView = () => { } gridContainer.innerHTML = html.join('') gridContainer.className = "grid-view" + gridContainer.addEventListener('pointerdown', gridDrag) let storyboarderSketchPane = document.querySelector("#storyboarder-sketch-pane") storyboarderSketchPane.appendChild(gridContainer) @@ -6617,7 +6712,7 @@ const renderGridView = () => { if (!isEditMode && selections.size <= 1) contextMenu.attachTo(e.currentTarget) // always track cursor position - updateThumbnailCursor(e.clientX, e.clientY) + updateGridViewCursor(e.clientX, e.clientY) if (e.button === 0) { editModeTimer = setTimeout(enableEditMode, enableEditModeDelay) @@ -6645,8 +6740,6 @@ const renderGridView = () => { // reset selections selections.clear() - console.log(e) - log.info(`Current board ${currentBoard} and index ${index}`) saveImageFile().then(() => { currentBoard = index renderThumbnailDrawerSelections() @@ -6662,10 +6755,13 @@ const cleanUpGridView = () => { let storyboarderSketchPane = document.querySelector("#storyboarder-sketch-pane") let gridView = storyboarderSketchPane.querySelector(".grid-view") if(!gridView) return + gridView.removeEventListener("pointerdown", gridDrag) + storyboarderSketchPane.removeChild(gridView) } // TODO(): Find a better way to switch modes let isGridViewMode = false +//#endregion const TimelineModeControlView = ({ mode = 'sequence', show = false }) => { let style = { display: show ? 'flex' : 'none' } diff --git a/src/main-window.html b/src/main-window.html index f5eeaf5e7d..197fbaafb5 100644 --- a/src/main-window.html +++ b/src/main-window.html @@ -256,6 +256,7 @@
+