| /* |
| * Copyright (C) 2017 Apple Inc. All rights reserved. |
| * |
| * Redistribution and use in source and binary forms, with or without |
| * modification, are permitted provided that the following conditions |
| * are met: |
| * 1. Redistributions of source code must retain the above copyright |
| * notice, this list of conditions and the following disclaimer. |
| * 2. Redistributions in binary form must reproduce the above copyright |
| * notice, this list of conditions and the following disclaimer in the |
| * documentation and/or other materials provided with the distribution. |
| * |
| * THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS'' |
| * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, |
| * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR |
| * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS |
| * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR |
| * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF |
| * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS |
| * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN |
| * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) |
| * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF |
| * THE POSSIBILITY OF SUCH DAMAGE. |
| */ |
| |
| WI.CanvasOverviewContentView = class CanvasOverviewContentView extends WI.CollectionContentView |
| { |
| constructor(representedObject) |
| { |
| console.assert(representedObject instanceof WI.CanvasCollection); |
| |
| let contentPlaceholder = WI.createMessageTextView(WI.UIString("No Canvas Contexts")); |
| let descriptionElement = contentPlaceholder.appendChild(document.createElement("div")); |
| descriptionElement.className = "description"; |
| descriptionElement.textContent = WI.UIString("Waiting for canvas contexts created by script or CSS."); |
| |
| let importNavigationItem = new WI.ButtonNavigationItem("import-recording", WI.UIString("Import"), "Images/Import.svg", 15, 15); |
| importNavigationItem.buttonStyle = WI.ButtonNavigationItem.Style.ImageAndText; |
| |
| let importHelpElement = WI.createNavigationItemHelp(WI.UIString("Press %s to load a recording from file."), importNavigationItem); |
| contentPlaceholder.appendChild(importHelpElement); |
| |
| super(representedObject, WI.CanvasContentView, contentPlaceholder); |
| |
| this.element.classList.add("canvas-overview"); |
| |
| if (WI.CanvasManager.supportsRecordingAutoCapture()) { |
| this._recordingAutoCaptureFrameCountInputElement = document.createElement("input"); |
| this._recordingAutoCaptureFrameCountInputElement.type = "number"; |
| this._recordingAutoCaptureFrameCountInputElement.min = 0; |
| this._recordingAutoCaptureFrameCountInputElement.style.setProperty("--recording-auto-capture-input-margin", CanvasOverviewContentView.recordingAutoCaptureInputMargin + "px"); |
| this._recordingAutoCaptureFrameCountInputElement.addEventListener("input", this._handleRecordingAutoCaptureInput.bind(this)); |
| this._recordingAutoCaptureFrameCountInputElementValue = WI.settings.canvasRecordingAutoCaptureFrameCount.value; |
| |
| const label = null; |
| this._recordingAutoCaptureNavigationItem = new WI.CheckboxNavigationItem("canvas-recording-auto-capture", label, !!WI.settings.canvasRecordingAutoCaptureEnabled.value); |
| this._recordingAutoCaptureNavigationItem.visibilityPriority = WI.NavigationItem.VisibilityPriority.Low; |
| this._recordingAutoCaptureNavigationItem.addEventListener(WI.CheckboxNavigationItem.Event.CheckedDidChange, this._handleRecordingAutoCaptureCheckedDidChange, this); |
| |
| let frameCount = this._updateRecordingAutoCaptureInputElementSize(); |
| this._setRecordingAutoCaptureFrameCount(frameCount); |
| this._updateRecordingAutoCaptureCheckboxLabel(frameCount); |
| } |
| |
| this._importButtonNavigationItem = new WI.ButtonNavigationItem("import-recording", WI.UIString("Import"), "Images/Import.svg", 15, 15); |
| this._importButtonNavigationItem.tooltip = WI.UIString("Import"); |
| this._importButtonNavigationItem.buttonStyle = WI.ButtonNavigationItem.Style.ImageAndText; |
| |
| this._refreshButtonNavigationItem = new WI.ButtonNavigationItem("refresh-all", WI.UIString("Refresh all"), "Images/ReloadFull.svg", 13, 13); |
| this._refreshButtonNavigationItem.enabled = false; |
| this._refreshButtonNavigationItem.addEventListener(WI.ButtonNavigationItem.Event.Clicked, this._refreshPreviews, this); |
| |
| this._showGridButtonNavigationItem = new WI.ActivateButtonNavigationItem("show-grid", WI.UIString("Show transparency grid"), WI.UIString("Hide transparency grid"), "Images/NavigationItemCheckers.svg", 13, 13); |
| this._showGridButtonNavigationItem.activated = !!WI.settings.showImageGrid.value; |
| this._showGridButtonNavigationItem.enabled = false; |
| this._showGridButtonNavigationItem.addEventListener(WI.ButtonNavigationItem.Event.Clicked, this._showGridButtonClicked, this); |
| |
| importNavigationItem.addEventListener(WI.ButtonNavigationItem.Event.Clicked, this._handleImportButtonNavigationItemClicked, this); |
| this._importButtonNavigationItem.addEventListener(WI.ButtonNavigationItem.Event.Clicked, this._handleImportButtonNavigationItemClicked, this); |
| |
| this._savedRecordingsContentView = null; |
| this._savedRecordingsTreeOutline = null; |
| } |
| |
| // Static |
| |
| static get recordingAutoCaptureInputMargin() { return 4; } |
| |
| // Public |
| |
| get navigationItems() |
| { |
| let navigationItems = [this._importButtonNavigationItem, new WI.DividerNavigationItem, this._refreshButtonNavigationItem, this._showGridButtonNavigationItem]; |
| if (WI.CanvasManager.supportsRecordingAutoCapture()) |
| navigationItems.unshift(this._recordingAutoCaptureNavigationItem, new WI.DividerNavigationItem); |
| return navigationItems; |
| } |
| |
| hidden() |
| { |
| WI.domManager.hideDOMNodeHighlight(); |
| |
| super.hidden(); |
| } |
| |
| // Protected |
| |
| contentViewAdded(contentView) |
| { |
| contentView.element.addEventListener("mouseenter", this._contentViewMouseEnter); |
| contentView.element.addEventListener("mouseleave", this._contentViewMouseLeave); |
| |
| if (this._savedRecordingsContentView) { |
| // Ensure that the imported recordings are always last. |
| this.removeSubview(this._savedRecordingsContentView); |
| this.addSubview(this._savedRecordingsContentView); |
| } |
| |
| this._updateNavigationItems(); |
| } |
| |
| contentViewRemoved(contentView) |
| { |
| contentView.element.removeEventListener("mouseenter", this._contentViewMouseEnter); |
| contentView.element.removeEventListener("mouseleave", this._contentViewMouseLeave); |
| |
| this._updateNavigationItems(); |
| } |
| |
| attached() |
| { |
| super.attached(); |
| |
| WI.settings.showImageGrid.addEventListener(WI.Setting.Event.Changed, this._updateShowImageGrid, this); |
| WI.settings.canvasRecordingAutoCaptureEnabled.addEventListener(WI.Setting.Event.Changed, this._handleCanvasRecordingAutoCaptureEnabledChanged, this); |
| WI.settings.canvasRecordingAutoCaptureFrameCount.addEventListener(WI.Setting.Event.Changed, this._handleCanvasRecordingAutoCaptureFrameCountChanged, this); |
| |
| WI.canvasManager.addEventListener(WI.CanvasManager.Event.RecordingSaved, this._handleRecordingSaved, this); |
| |
| if (this._savedRecordingsTreeOutline) |
| this._savedRecordingsTreeOutline.removeChildren(); |
| for (let recording of WI.canvasManager.savedRecordings) |
| this._addSavedRecording(recording); |
| } |
| |
| detached() |
| { |
| WI.canvasManager.removeEventListener(null, null, this); |
| |
| WI.settings.canvasRecordingAutoCaptureFrameCount.removeEventListener(null, null, this); |
| WI.settings.canvasRecordingAutoCaptureEnabled.removeEventListener(null, null, this); |
| WI.settings.showImageGrid.removeEventListener(null, null, this); |
| |
| super.detached(); |
| } |
| |
| // Private |
| |
| get _itemMargin() |
| { |
| return parseInt(window.getComputedStyle(this.element).getPropertyValue("--item-margin")); |
| } |
| |
| _refreshPreviews() |
| { |
| for (let canvasContentView of this.subviews) |
| canvasContentView.refreshPreview(); |
| } |
| |
| _updateNavigationItems() |
| { |
| let hasItems = !!this.representedObject.size; |
| this._refreshButtonNavigationItem.enabled = hasItems; |
| this._showGridButtonNavigationItem.enabled = hasItems; |
| } |
| |
| _showGridButtonClicked(event) |
| { |
| WI.settings.showImageGrid.value = !this._showGridButtonNavigationItem.activated; |
| } |
| |
| _updateShowImageGrid() |
| { |
| this._showGridButtonNavigationItem.activated = !!WI.settings.showImageGrid.value; |
| } |
| |
| _contentViewMouseEnter(event) |
| { |
| let contentView = WI.View.fromElement(event.target); |
| if (!(contentView instanceof WI.CanvasContentView)) |
| return; |
| |
| let canvas = contentView.representedObject; |
| if (canvas.cssCanvasName || canvas.contextType === WI.Canvas.ContextType.WebGPU) { |
| canvas.requestClientNodes((clientNodes) => { |
| WI.domManager.highlightDOMNodeList(clientNodes.map((node) => node.id)); |
| }); |
| return; |
| } |
| |
| canvas.requestNode().then((node) => { |
| if (!node || !node.ownerDocument) |
| return; |
| WI.domManager.highlightDOMNode(node.id); |
| }); |
| } |
| |
| _contentViewMouseLeave(event) |
| { |
| WI.domManager.hideDOMNodeHighlight(); |
| } |
| |
| _setRecordingAutoCaptureFrameCount(frameCount) |
| { |
| console.assert(!isNaN(frameCount) && frameCount >= 0); |
| |
| if (this._recordingAutoCaptureNavigationItem.checked) |
| frameCount = Math.max(1, frameCount); |
| |
| let enabled = frameCount > 0 && !!this._recordingAutoCaptureNavigationItem.checked; |
| |
| WI.canvasManager.setRecordingAutoCaptureFrameCount(enabled, frameCount); |
| } |
| |
| _updateRecordingAutoCaptureCheckboxLabel(frameCount) |
| { |
| let active = document.activeElement === this._recordingAutoCaptureFrameCountInputElement; |
| let selectionStart = this._recordingAutoCaptureFrameCountInputElement.selectionStart; |
| let selectionEnd = this._recordingAutoCaptureFrameCountInputElement.selectionEnd; |
| let direction = this._recordingAutoCaptureFrameCountInputElement.direction; |
| |
| let label = frameCount === 1 ? WI.UIString("Record first %s frame") : WI.UIString("Record first %s frames"); |
| let fragment = document.createDocumentFragment(); |
| String.format(label, [this._recordingAutoCaptureFrameCountInputElement], String.standardFormatters, fragment, (a, b) => { |
| a.append(b); |
| return a; |
| }); |
| this._recordingAutoCaptureNavigationItem.label = fragment; |
| |
| if (active) { |
| this._recordingAutoCaptureFrameCountInputElement.selectionStart = selectionStart; |
| this._recordingAutoCaptureFrameCountInputElement.selectionEnd = selectionEnd; |
| this._recordingAutoCaptureFrameCountInputElement.direction = direction; |
| } |
| } |
| |
| get _recordingAutoCaptureFrameCountInputElementValue() |
| { |
| return parseInt(this._recordingAutoCaptureFrameCountInputElement.value); |
| } |
| |
| set _recordingAutoCaptureFrameCountInputElementValue(frameCount) |
| { |
| if (this._recordingAutoCaptureFrameCountInputElement.value || frameCount) |
| this._recordingAutoCaptureFrameCountInputElement.value = frameCount; |
| |
| this._recordingAutoCaptureFrameCountInputElement.placeholder = frameCount; |
| } |
| |
| _updateRecordingAutoCaptureInputElementSize() |
| { |
| let frameCount = this._recordingAutoCaptureFrameCountInputElementValue; |
| if (isNaN(frameCount) || frameCount < 0) { |
| frameCount = 0; |
| this._recordingAutoCaptureFrameCountInputElementValue = frameCount; |
| } |
| |
| WI.ImageUtilities.scratchCanvasContext2D((context) => { |
| if (!this._recordingAutoCaptureFrameCountInputElement.__cachedFont) { |
| let computedStyle = window.getComputedStyle(this._recordingAutoCaptureFrameCountInputElement); |
| this._recordingAutoCaptureFrameCountInputElement.__cachedFont = computedStyle.font; |
| } |
| |
| context.font = this._recordingAutoCaptureFrameCountInputElement.__cachedFont; |
| let textMetrics = context.measureText(this._recordingAutoCaptureFrameCountInputElement.value || this._recordingAutoCaptureFrameCountInputElement.placeholder); |
| this._recordingAutoCaptureFrameCountInputElement.style.setProperty("width", (textMetrics.width + (2 * CanvasOverviewContentView.recordingAutoCaptureInputMargin)) + "px"); |
| }); |
| |
| return frameCount; |
| } |
| |
| _addSavedRecording(recording) |
| { |
| console.assert(!recording.source); |
| |
| if (!this._savedRecordingsContentView) { |
| this._savedRecordingsContentView = new WI.ContentView; |
| this._savedRecordingsContentView.element.classList.add("canvas", "saved-recordings"); |
| this.addSubview(this._savedRecordingsContentView); |
| |
| let header = this._savedRecordingsContentView.element.appendChild(document.createElement("header")); |
| header.textContent = WI.UIString("Saved Recordings"); |
| |
| this.hideContentPlaceholder(); |
| } |
| |
| if (!this._savedRecordingsTreeOutline) { |
| const selectable = false; |
| this._savedRecordingsTreeOutline = new WI.TreeOutline(selectable); |
| this._savedRecordingsTreeOutline.addEventListener(WI.TreeOutline.Event.ElementClicked, this._handleSavedRecordingClicked, this); |
| this._savedRecordingsContentView.element.appendChild(this._savedRecordingsTreeOutline.element); |
| } |
| |
| const subtitle = null; |
| let recordingTreeElement = new WI.GeneralTreeElement(["recording"], recording.displayName, subtitle, recording); |
| recordingTreeElement.selectable = false; |
| this._savedRecordingsTreeOutline.appendChild(recordingTreeElement); |
| } |
| |
| _handleRecordingAutoCaptureInput(event) |
| { |
| let frameCount = this._updateRecordingAutoCaptureInputElementSize(); |
| this._recordingAutoCaptureNavigationItem.checked = !!frameCount; |
| |
| this._setRecordingAutoCaptureFrameCount(frameCount); |
| } |
| |
| _handleRecordingAutoCaptureCheckedDidChange(event) |
| { |
| this._setRecordingAutoCaptureFrameCount(this._recordingAutoCaptureFrameCountInputElementValue || 0); |
| } |
| |
| _handleCanvasRecordingAutoCaptureEnabledChanged(event) |
| { |
| this._recordingAutoCaptureNavigationItem.checked = WI.settings.canvasRecordingAutoCaptureEnabled.value; |
| } |
| |
| _handleCanvasRecordingAutoCaptureFrameCountChanged(event) |
| { |
| // Only update the value if it is different to prevent mangling the selection. |
| if (this._recordingAutoCaptureFrameCountInputElementValue !== WI.settings.canvasRecordingAutoCaptureFrameCount.value) |
| this._recordingAutoCaptureFrameCountInputElementValue = WI.settings.canvasRecordingAutoCaptureFrameCount.value; |
| |
| this._updateRecordingAutoCaptureCheckboxLabel(WI.settings.canvasRecordingAutoCaptureFrameCount.value); |
| } |
| |
| _handleImportButtonNavigationItemClicked(event) |
| { |
| WI.FileUtilities.importJSON((result) => WI.canvasManager.processJSON(result)); |
| } |
| |
| _handleRecordingSaved(event) |
| { |
| this._addSavedRecording(event.data.recording); |
| } |
| |
| _handleSavedRecordingClicked(event) |
| { |
| WI.showRepresentedObject(event.data.treeElement.representedObject); |
| } |
| }; |