diff --git a/dist/ueblueprint.js b/dist/ueblueprint.js index fdb0d5b..1eb1117 100644 --- a/dist/ueblueprint.js +++ b/dist/ueblueprint.js @@ -68,7 +68,6 @@ class Utility { return getComputedStyle(element).getPropertyValue('--ueb-scale') } - /** * Sets a value in an object * @param {String[]} keys The chained keys to access from object in order to set the value @@ -92,7 +91,6 @@ class Utility { return false } - /** * Gets a value from an object, gives defaultValue in case of failure * @param {Object} source Object holding the data @@ -272,7 +270,6 @@ class LocalizedTextEntity extends Entity { toString() { "NSLOCTEXT(" + `"${this.namespace}"` + ", " + `"${this.key}"` + ", " + `"${this.value}"` + ")"; } - } class PinReferenceEntity extends Entity { @@ -727,6 +724,22 @@ End Object`; } } +/** @typedef {import("./graph/GraphNode").default} GraphNode */ +class BlueprintData { + + constructor() { + /** @type {GraphNode[]}" */ + this.nodes = new Array(); + this.expandGridSize = 400; + /** @type {number[]} */ + this.additional = /*[2 * this.expandGridSize, 2 * this.expandGridSize]*/[0, 0]; + /** @type {number[]} */ + this.translateValue = /*[this.expandGridSize, this.expandGridSize]*/[0, 0]; + /** @type {number[]} */ + this.mousePosition = [0, 0]; + } +} + /** * @typedef {import(""../entity/Entity"").default} Entity */ @@ -808,18 +821,23 @@ class Context { this.options = options; if (options?.wantsFocusCallback ?? false) { let self = this; - this.blueprint.addEventListener("blueprintfocus", _ => self.blueprintFocused()); - this.blueprint.addEventListener("blueprintunfocus", _ => self.blueprintUnfocused()); + this.blueprintfocusHandler = _ => self.blueprintFocused(); + this.blueprintunfocusHandler = _ => self.blueprintUnfocused(); + this.blueprint.addEventListener("blueprintfocus", this.blueprintfocusHandler); + this.blueprint.addEventListener("blueprintunfocus", this.blueprintunfocusHandler); } } + unlistenDOMElement() { + this.blueprint.removeEventListener("blueprintfocus", this.blueprintfocusHandler); + this.blueprint.removeEventListener("blueprintunfocus", this.blueprintunfocusHandler); + } + blueprintFocused() { - console.log("focused"); } blueprintUnfocused() { } - } class Pointing extends Context { @@ -829,6 +847,11 @@ class Pointing extends Context { this.movementSpace = this.blueprint?.getGridDOMElement() ?? document.documentElement; } + /** + * + * @param {MouseEvent} mouseEvent + * @returns + */ getLocation(mouseEvent) { const scaleCorrection = 1 / Utility.getScale(this.target); const targetOffset = this.movementSpace.getBoundingClientRect(); @@ -844,6 +867,7 @@ class Pointing extends Context { * This class manages the ui gesture of mouse click and drag. Tha actual operations are implemented by the subclasses. */ class MouseClickDrag extends Pointing { + constructor(target, blueprint, options) { super(target, blueprint, options); this.clickButton = options?.clickButton ?? 0; @@ -852,6 +876,7 @@ class MouseClickDrag extends Pointing { this.looseTarget = options?.looseTarget ?? false; this.started = false; this.clickedPosition = [0, 0]; + const movementListenedElement = this.moveEverywhere ? document.documentElement : this.movementSpace; let self = this; @@ -920,10 +945,11 @@ class MouseClickDrag extends Pointing { } unlistenDOMElement() { + super.unlistenDOMElement(); this.target.removeEventListener('mousedown', this.mouseDownHandler); if (this.clickButton == 2) { this.target.removeEventListener('contextmenu', this.preventDefault); - } + } blueprintunfocusHandler; } /* Subclasses will override the following methods */ @@ -945,10 +971,10 @@ class DragScroll extends MouseClickDrag { dragTo(location, movement) { this.blueprint.scrollDelta([-movement[0], -movement[1]]); } - } class GraphElement extends HTMLElement { + /** * * @param {import("../template/Template").default} template The template to render this node @@ -1335,92 +1361,24 @@ class GraphSelector extends GraphElement { customElements.define('u-selector', GraphSelector); -class Select extends MouseClickDrag { - - constructor(target, blueprint, options) { - super(target, blueprint, options); - this.stepSize = options?.stepSize; - this.mousePosition = [0, 0]; - this.selectorElement = this.blueprint.selectorElement; - } - - startDrag() { - this.selectorElement.startSelecting(this.clickedPosition); - } - - dragTo(location, movement) { - this.selectorElement.doSelecting(location); - } - - endDrag() { - if (this.started) { - this.selectorElement.finishSelecting(); - } else { - this.blueprint.unselectAll(); - } - } -} +class MouseTracking extends Pointing { -class MouseWheel extends Pointing { - - /** - * - * @param {HTMLElement} target - * @param {import("../Blueprint").default} blueprint - * @param {Object} options - */ - constructor(target, blueprint, options) { - options.wantsFocusCallback = true; - super(target, blueprint, options); - this.looseTarget = options?.looseTarget ?? true; - let self = this; - - this.mouseWheelHandler = e => { - e.preventDefault(); - const location = self.getLocation(e); - self.wheel(Math.sign(e.deltaY), location); - }; - this.mouseParentWheelHandler = e => e.preventDefault(); - - if (this.blueprint.focused) { - this.movementSpace.addEventListener('wheel', this.mouseWheelHandler, false); - } - } - - blueprintFocused() { - this.movementSpace.addEventListener('wheel', this.mouseWheelHandler, false); - this.movementSpace.parentElement?.addEventListener('wheel', this.mouseParentWheelHandler); - } - - blueprintUnfocused() { - this.movementSpace.removeEventListener('wheel', this.mouseWheelHandler, false); - this.movementSpace.parentElement?.removeEventListener('wheel', this.mouseParentWheelHandler); - } - - /* Subclasses will override the following method */ - wheel(variation, location) { - } -} + constructor(target, blueprint, options = {}) { + options.wantsFocusCallback = true; + super(target, blueprint, options); -class Zoom extends MouseWheel { - wheel(variation, location) { - let zoomLevel = this.blueprint.getZoom(); - zoomLevel -= variation; - this.blueprint.setZoom(zoomLevel, location); - } -} + let self = this; + this.mousemoveHandler = e => { + self.blueprint.entity.mousePosition = self.getLocation(e); + }; + } -/** @typedef {import("./graph/GraphNode").default} GraphNode */ -class BlueprintData { + blueprintFocused() { + this.target.addEventListener("mousemove", this.mousemoveHandler); + } - constructor() { - /** @type {GraphNode[]}" */ - this.nodes = new Array(); - this.expandGridSize = 400; - /** @type {Array} */ - this.additional = /*[2 * this.expandGridSize, 2 * this.expandGridSize]*/[0, 0]; - /** @type {Array} */ - this.translateValue = /*[this.expandGridSize, this.expandGridSize]*/[0, 0]; + blueprintUnfocused() { + this.target.removeEventListener("mousemove", this.mousemoveHandler); } } @@ -1495,6 +1453,7 @@ class NodeTemplate extends Template { } class Drag extends MouseClickDrag { + constructor(target, blueprint, options) { super(target, blueprint, options); this.stepSize = parseInt(options?.stepSize); @@ -1599,7 +1558,6 @@ class SelectableDraggable extends GraphElement { this.blueprint.removeEventListener('uDragSelected', this.dragHandler); } } - } class GraphNode extends SelectableDraggable { @@ -1609,11 +1567,16 @@ class GraphNode extends SelectableDraggable { return new GraphNode(entity) } + /** + * + * @param {ObjectEntity} entity + */ constructor(entity) { super(entity, new NodeTemplate()); this.graphNodeName = 'n/a'; this.inputs = []; this.outputs = []; + super.setLocation([this.entity.NodePosX, this.entity.NodePosY]); } connectedCallback() { @@ -1626,68 +1589,78 @@ class GraphNode extends SelectableDraggable { this.style.setProperty('--ueb-position-x', this.location[0]); this.style.setProperty('--ueb-position-y', this.location[1]); } + + setLocation(value = [0, 0]) { + this.entity.NodePosX = value[0]; + this.entity.NodePosY = value[1]; + super.setLocation(value); + } } customElements.define('u-node', GraphNode); -class KeyboardShortcut extends Context { +class Paste extends Context { - constructor(target, blueprint, options) { + constructor(target, blueprint, options = {}) { + options.wantsFocusCallback = true; super(target, blueprint, options); - /** @type {String[]} */ - this.keys = this.options?.keys ?? []; - /** @type Numeric */ - this.currentKey = 0; + this.serializer = new ObjectSerializer(); let self = this; - this.keyDownHandler = e => { - e.preventDefault(); - if (Utility.equals(e.keys[self.currentKey], e.key)) { - if (++self.currentKey == this.keys.length) { - self.fire(); - } - } - }; - this.keyUpHandler = e => { - e.preventDefault(); - for (let i = 0; i < self.currentKey; ++i) { - if (Utility.equals(e.keys[self.currentKey], e.key)) { - self.currentKey = i; - break - } - } - }; - if (this.keys.length > 0) { - this.target.addEventListener("keydown", this.keyDownHandler); - this.target.addEventListener("keyup", this.keyUpHandler); - } + this.pasteHandle = e => self.pasted(e.clipboardData.getData("Text")); } - unlistenDOMElement() { - this.target.removeEventListener('keydown', this.keyDownHandler); - this.target.removeEventListener('keyup', this.keyUpHandler); + blueprintFocused() { + document.body.addEventListener("paste", this.pasteHandle); } - fire() { + blueprintUnfocused() { + document.body.removeEventListener("paste", this.pasteHandle); } -} - -class Paste extends KeyboardShortcut { - - constructor(target, blueprint) { - super(target, blueprint, { - keys: ["Control", "C"] + pasted(value) { + let top = Number.MAX_SAFE_INTEGER; + let left = Number.MAX_SAFE_INTEGER; + let nodes = this.serializer.readMultiple(value).map(entity => { + let node = new GraphNode(entity); + top = Math.min(top, node.location[1]); + left = Math.min(left, node.location[0]); + return node + }); + let mousePosition = this.blueprint.entity.mousePosition; + nodes.forEach(node => { + node.location[0] += mousePosition[0] - left; + node.location[1] += mousePosition[1] - top; }); - } - - fire() { - let value = navigator.clipboard.readText(); - let nodes = this.serializer.readMultiple(value).map(entity => new GraphNode(entity)); this.blueprint.addNode(...nodes); } +} +class Select extends MouseClickDrag { + + constructor(target, blueprint, options) { + super(target, blueprint, options); + this.stepSize = options?.stepSize; + this.mousePosition = [0, 0]; + this.selectorElement = this.blueprint.selectorElement; + } + + startDrag() { + this.selectorElement.startSelecting(this.clickedPosition); + } + + dragTo(location, movement) { + this.selectorElement.doSelecting(location); + } + + endDrag() { + if (this.started) { + this.selectorElement.finishSelecting(); + } else { + this.blueprint.unselectAll(); + } + } } class Unfocus extends Context { @@ -1724,6 +1697,56 @@ class Unfocus extends Context { } } +class MouseWheel extends Pointing { + + /** + * + * @param {HTMLElement} target + * @param {import("../Blueprint").default} blueprint + * @param {Object} options + */ + constructor(target, blueprint, options) { + options.wantsFocusCallback = true; + super(target, blueprint, options); + this.looseTarget = options?.looseTarget ?? true; + let self = this; + + this.mouseWheelHandler = e => { + e.preventDefault(); + const location = self.getLocation(e); + self.wheel(Math.sign(e.deltaY), location); + }; + this.mouseParentWheelHandler = e => e.preventDefault(); + + if (this.blueprint.focused) { + this.movementSpace.addEventListener('wheel', this.mouseWheelHandler, false); + } + } + + blueprintFocused() { + this.movementSpace.addEventListener('wheel', this.mouseWheelHandler, false); + this.movementSpace.parentElement?.addEventListener('wheel', this.mouseParentWheelHandler); + } + + blueprintUnfocused() { + this.movementSpace.removeEventListener('wheel', this.mouseWheelHandler, false); + this.movementSpace.parentElement?.removeEventListener('wheel', this.mouseParentWheelHandler); + } + + /* Subclasses will override the following method */ + wheel(variation, location) { + } +} + +class Zoom extends MouseWheel { + + wheel(variation, location) { + let zoomLevel = this.blueprint.getZoom(); + zoomLevel -= variation; + this.blueprint.setZoom(zoomLevel, location); + } +} + /** @typedef {import("./graph/GraphNode").default} GraphNode */ class Blueprint extends GraphElement { @@ -1783,6 +1806,7 @@ class Blueprint extends GraphElement { this.nodesContainerElement.append(this.selectorElement); this.querySelector('[data-nodes]').append(...this.entity.nodes); + this.pasteObject = new Paste(this.getGridDOMElement(), this); this.dragObject = new DragScroll(this.getGridDOMElement(), this, { @@ -1802,6 +1826,7 @@ class Blueprint extends GraphElement { }); this.unfocusObject = new Unfocus(this.getGridDOMElement(), this); + this.mouseTrackingObject = new MouseTracking(this.getGridDOMElement(), this); } getGridDOMElement() { @@ -1810,6 +1835,7 @@ class Blueprint extends GraphElement { disconnectedCallback() { super.disconnectedCallback(); + setSelected(false); this.dragObject.unlistenDOMElement(); this.selectObject.unlistenDOMElement(); this.pasteObject.unlistenDOMElement(); diff --git a/js/Blueprint.js b/js/Blueprint.js index fae734e..6bcd6ff 100755 --- a/js/Blueprint.js +++ b/js/Blueprint.js @@ -1,13 +1,14 @@ +import BlueprintData from "./BlueprintData" import BlueprintTemplate from "./template/BlueprintTemplate" import DragScroll from "./input/DragScroll" import GraphElement from "./graph/GraphElement" import GraphSelector from "./graph/GraphSelector" +import MouseTracking from "./input/MouseTracking" +import Paste from "./input/Paste" import Select from "./input/Select" +import Unfocus from "./input/Unfocus" import Utility from "./Utility" import Zoom from "./input/Zoom" -import BlueprintData from "./BlueprintData" -import Paste from "./input/Paste" -import Unfocus from "./input/Unfocus" /** @typedef {import("./graph/GraphNode").default} GraphNode */ export default class Blueprint extends GraphElement { @@ -68,6 +69,7 @@ export default class Blueprint extends GraphElement { this.nodesContainerElement.append(this.selectorElement) this.querySelector('[data-nodes]').append(...this.entity.nodes) + this.pasteObject = new Paste(this.getGridDOMElement(), this) this.dragObject = new DragScroll(this.getGridDOMElement(), this, { @@ -87,6 +89,7 @@ export default class Blueprint extends GraphElement { }) this.unfocusObject = new Unfocus(this.getGridDOMElement(), this) + this.mouseTrackingObject = new MouseTracking(this.getGridDOMElement(), this) } getGridDOMElement() { @@ -95,6 +98,7 @@ export default class Blueprint extends GraphElement { disconnectedCallback() { super.disconnectedCallback() + setSelected(false) this.dragObject.unlistenDOMElement() this.selectObject.unlistenDOMElement() this.pasteObject.unlistenDOMElement() diff --git a/js/BlueprintData.js b/js/BlueprintData.js index 7335eef..2a4eff7 100644 --- a/js/BlueprintData.js +++ b/js/BlueprintData.js @@ -5,9 +5,11 @@ export default class BlueprintData { /** @type {GraphNode[]}" */ this.nodes = new Array() this.expandGridSize = 400 - /** @type {Array} */ + /** @type {number[]} */ this.additional = /*[2 * this.expandGridSize, 2 * this.expandGridSize]*/[0, 0] - /** @type {Array} */ + /** @type {number[]} */ this.translateValue = /*[this.expandGridSize, this.expandGridSize]*/[0, 0] + /** @type {number[]} */ + this.mousePosition = [0, 0] } } \ No newline at end of file diff --git a/js/Utility.js b/js/Utility.js index 9d35262..304e515 100755 --- a/js/Utility.js +++ b/js/Utility.js @@ -9,7 +9,6 @@ export default class Utility { return getComputedStyle(element).getPropertyValue('--ueb-scale') } - /** * Sets a value in an object * @param {String[]} keys The chained keys to access from object in order to set the value @@ -33,7 +32,6 @@ export default class Utility { return false } - /** * Gets a value from an object, gives defaultValue in case of failure * @param {Object} source Object holding the data diff --git a/js/entity/Entity.js b/js/entity/Entity.js index 603f986..ca333e0 100755 --- a/js/entity/Entity.js +++ b/js/entity/Entity.js @@ -2,6 +2,7 @@ import TypeInitialization from "./TypeInitialization" import Utility from "../Utility" export default class Entity { + constructor(options = {}) { /** * diff --git a/js/entity/LocalizedTextEntity.js b/js/entity/LocalizedTextEntity.js index 0209c5a..8dbf985 100755 --- a/js/entity/LocalizedTextEntity.js +++ b/js/entity/LocalizedTextEntity.js @@ -15,5 +15,4 @@ export default class LocalizedTextEntity extends Entity { toString() { "NSLOCTEXT(" + `"${this.namespace}"` + ", " + `"${this.key}"` + ", " + `"${this.value}"` + ")" } - } diff --git a/js/entity/primitive/Primitive.js b/js/entity/primitive/Primitive.js index 4d68d8b..995cefb 100755 --- a/js/entity/primitive/Primitive.js +++ b/js/entity/primitive/Primitive.js @@ -1,4 +1,5 @@ export default class Primitive { + toString() { return "Unimplemented for " + this.constructor.name } diff --git a/js/graph/GraphElement.js b/js/graph/GraphElement.js index 4427c5e..79a7b21 100755 --- a/js/graph/GraphElement.js +++ b/js/graph/GraphElement.js @@ -1,4 +1,5 @@ export default class GraphElement extends HTMLElement { + /** * * @param {import("../template/Template").default} template The template to render this node diff --git a/js/graph/GraphNode.js b/js/graph/GraphNode.js index e960847..eb55310 100755 --- a/js/graph/GraphNode.js +++ b/js/graph/GraphNode.js @@ -1,7 +1,7 @@ -import ObjectEntity from "../entity/ObjectEntity" -import SerializerFactory from "../serialization/SerializerFactory" import NodeTemplate from "../template/NodeTemplate" +import ObjectEntity from "../entity/ObjectEntity" import SelectableDraggable from "./SelectableDraggable" +import SerializerFactory from "../serialization/SerializerFactory" export default class GraphNode extends SelectableDraggable { @@ -10,11 +10,16 @@ export default class GraphNode extends SelectableDraggable { return new GraphNode(entity) } + /** + * + * @param {ObjectEntity} entity + */ constructor(entity) { super(entity, new NodeTemplate()) this.graphNodeName = 'n/a' this.inputs = [] this.outputs = [] + super.setLocation([this.entity.NodePosX, this.entity.NodePosY]) } connectedCallback() { @@ -27,6 +32,12 @@ export default class GraphNode extends SelectableDraggable { this.style.setProperty('--ueb-position-x', this.location[0]) this.style.setProperty('--ueb-position-y', this.location[1]) } + + setLocation(value = [0, 0]) { + this.entity.NodePosX = value[0] + this.entity.NodePosY = value[1] + super.setLocation(value) + } } customElements.define('u-node', GraphNode) diff --git a/js/graph/SelectableDraggable.js b/js/graph/SelectableDraggable.js index ee0251b..c0e52cd 100755 --- a/js/graph/SelectableDraggable.js +++ b/js/graph/SelectableDraggable.js @@ -66,5 +66,4 @@ export default class SelectableDraggable extends GraphElement { this.blueprint.removeEventListener('uDragSelected', this.dragHandler) } } - } diff --git a/js/input/Context.js b/js/input/Context.js index 73cb793..817a5dc 100644 --- a/js/input/Context.js +++ b/js/input/Context.js @@ -8,16 +8,21 @@ export default class Context { this.options = options if (options?.wantsFocusCallback ?? false) { let self = this - this.blueprint.addEventListener("blueprintfocus", _ => self.blueprintFocused()) - this.blueprint.addEventListener("blueprintunfocus", _ => self.blueprintUnfocused()) + this.blueprintfocusHandler = _ => self.blueprintFocused() + this.blueprintunfocusHandler = _ => self.blueprintUnfocused() + this.blueprint.addEventListener("blueprintfocus", this.blueprintfocusHandler) + this.blueprint.addEventListener("blueprintunfocus", this.blueprintunfocusHandler) } } + unlistenDOMElement() { + this.blueprint.removeEventListener("blueprintfocus", this.blueprintfocusHandler) + this.blueprint.removeEventListener("blueprintunfocus", this.blueprintunfocusHandler) + } + blueprintFocused() { - console.log("focused") } blueprintUnfocused() { } - } diff --git a/js/input/DragScroll.js b/js/input/DragScroll.js index dbfc9bd..0358692 100755 --- a/js/input/DragScroll.js +++ b/js/input/DragScroll.js @@ -5,5 +5,4 @@ export default class DragScroll extends MouseClickDrag { dragTo(location, movement) { this.blueprint.scrollDelta([-movement[0], -movement[1]]) } - } diff --git a/js/input/KeyboardShortcut.js b/js/input/KeyboardShortcut.js index 8868eff..ff7d893 100644 --- a/js/input/KeyboardShortcut.js +++ b/js/input/KeyboardShortcut.js @@ -1,46 +1,41 @@ import Context from "./Context" -import Utility from "../Utility" export default class KeyboardShortcut extends Context { constructor(target, blueprint, options) { + options.wantsFocusCallback = true super(target, blueprint, options) - /** @type {String[]} */ - this.keys = this.options?.keys ?? [] - /** @type Numeric */ - this.currentKey = 0 + /** @type {String[]} */ + this.key = this.options.key + this.ctrlKey = options.ctrlKey ?? false + this.shiftKey = options.shiftKey ?? false + this.altKey = options.altKey ?? false + this.metaKey = options.metaKey ?? false let self = this this.keyDownHandler = e => { e.preventDefault() - if (Utility.equals(e.keys[self.currentKey], e.key)) { - if (++self.currentKey == this.keys.length) { - self.fire() - } + if ( + e.code == self.key + && e.ctrlKey === self.ctrlKey + && e.shiftKey === self.shiftKey + && e.altKey === self.altKey + && e.metaKey === self.metaKey + ) { + self.fire() } } - this.keyUpHandler = e => { - e.preventDefault() - for (let i = 0; i < self.currentKey; ++i) { - if (Utility.equals(e.keys[self.currentKey], e.key)) { - self.currentKey = i - break - } - } - } - if (this.keys.length > 0) { - this.target.addEventListener("keydown", this.keyDownHandler) - this.target.addEventListener("keyup", this.keyUpHandler) - } } - unlistenDOMElement() { - this.target.removeEventListener('keydown', this.keyDownHandler) - this.target.removeEventListener('keyup', this.keyUpHandler) + blueprintFocused() { + document.addEventListener('keydown', this.keyDownHandler) + } + + blueprintUnfocused() { + document.removeEventListener('keydown', this.keyDownHandler) } fire() { } - } diff --git a/js/input/MouseClickDrag.js b/js/input/MouseClickDrag.js index 7cce94c..d9b621a 100755 --- a/js/input/MouseClickDrag.js +++ b/js/input/MouseClickDrag.js @@ -13,6 +13,7 @@ export default class MouseClickDrag extends Pointing { this.looseTarget = options?.looseTarget ?? false this.started = false this.clickedPosition = [0, 0] + const movementListenedElement = this.moveEverywhere ? document.documentElement : this.movementSpace let self = this @@ -81,10 +82,11 @@ export default class MouseClickDrag extends Pointing { } unlistenDOMElement() { + super.unlistenDOMElement() this.target.removeEventListener('mousedown', this.mouseDownHandler) if (this.clickButton == 2) { this.target.removeEventListener('contextmenu', this.preventDefault) - } + } blueprintunfocusHandler } /* Subclasses will override the following methods */ diff --git a/js/input/MouseTracking.js b/js/input/MouseTracking.js new file mode 100644 index 0000000..c4c3111 --- /dev/null +++ b/js/input/MouseTracking.js @@ -0,0 +1,22 @@ +import Pointing from "./Pointing" + +export default class MouseTracking extends Pointing { + + constructor(target, blueprint, options = {}) { + options.wantsFocusCallback = true + super(target, blueprint, options) + + let self = this + this.mousemoveHandler = e => { + self.blueprint.entity.mousePosition = self.getLocation(e) + } + } + + blueprintFocused() { + this.target.addEventListener("mousemove", this.mousemoveHandler) + } + + blueprintUnfocused() { + this.target.removeEventListener("mousemove", this.mousemoveHandler) + } +} \ No newline at end of file diff --git a/js/input/Paste.js b/js/input/Paste.js index 04be027..80f0d5b 100644 --- a/js/input/Paste.js +++ b/js/input/Paste.js @@ -1,18 +1,41 @@ import GraphNode from "../graph/GraphNode" -import KeyboardShortcut from "./KeyboardShortcut" +import ObjectSerializer from "../serialization/ObjectSerializer" +import Context from "./Context" -export default class Paste extends KeyboardShortcut { +export default class Paste extends Context { - constructor(target, blueprint) { - super(target, blueprint, { - keys: ["Control", "C"] - }) + constructor(target, blueprint, options = {}) { + options.wantsFocusCallback = true + super(target, blueprint, options) + + this.serializer = new ObjectSerializer() + + let self = this + this.pasteHandle = e => self.pasted(e.clipboardData.getData("Text")) } - fire() { - let value = navigator.clipboard.readText() - let nodes = this.serializer.readMultiple(value).map(entity => new GraphNode(entity)) + blueprintFocused() { + document.body.addEventListener("paste", this.pasteHandle) + } + + blueprintUnfocused() { + document.body.removeEventListener("paste", this.pasteHandle) + } + + pasted(value) { + let top = Number.MAX_SAFE_INTEGER + let left = Number.MAX_SAFE_INTEGER + let nodes = this.serializer.readMultiple(value).map(entity => { + let node = new GraphNode(entity) + top = Math.min(top, node.location[1]) + left = Math.min(left, node.location[0]) + return node + }) + let mousePosition = this.blueprint.entity.mousePosition + nodes.forEach(node => { + node.location[0] += mousePosition[0] - left + node.location[1] += mousePosition[1] - top + }) this.blueprint.addNode(...nodes) } - } diff --git a/js/input/Pointing.js b/js/input/Pointing.js index 569f92e..affea89 100755 --- a/js/input/Pointing.js +++ b/js/input/Pointing.js @@ -8,6 +8,11 @@ export default class Pointing extends Context { this.movementSpace = this.blueprint?.getGridDOMElement() ?? document.documentElement } + /** + * + * @param {MouseEvent} mouseEvent + * @returns + */ getLocation(mouseEvent) { const scaleCorrection = 1 / Utility.getScale(this.target) const targetOffset = this.movementSpace.getBoundingClientRect() diff --git a/js/serialization/Serializer.js b/js/serialization/Serializer.js index c0dbcea..d65fa57 100755 --- a/js/serialization/Serializer.js +++ b/js/serialization/Serializer.js @@ -6,7 +6,6 @@ import SerializerFactory from "./SerializerFactory" import TypeInitialization from "../entity/TypeInitialization" import Utility from "../Utility" - export default class Serializer { static grammar = Parsimmon.createLanguage(new Grammar()) diff --git a/js/serialization/SerializerFactory.js b/js/serialization/SerializerFactory.js index 8f4c893..78a4319 100755 --- a/js/serialization/SerializerFactory.js +++ b/js/serialization/SerializerFactory.js @@ -1,6 +1,5 @@ import Utility from "../Utility" - export default class SerializerFactory { static #serializers = new Map() diff --git a/ueblueprint.html b/ueblueprint.html index a17f869..729d34b 100755 --- a/ueblueprint.html +++ b/ueblueprint.html @@ -18,8 +18,8 @@ let node1 = GraphNode.fromSerializedObject(`Begin Object Class=/Script/BlueprintGraph.K2Node_CommutativeAssociativeBinaryOperator Name="K2Node_CommutativeAssociativeBinaryOperator_1" bIsPureFunc=True FunctionReference=(MemberParent=Class'"/Script/Engine.KismetStringLibrary"',MemberName="Concat_StrStr") - NodePosX=1856 - NodePosY=1792 + NodePosX=30 + NodePosY=50 NodeGuid=43D899AC42EB640EF98BFCA1597FD6C9 CustomProperties Pin (PinId=FB3490034B2B6127D29E1397E04F6BD6,PinName="self",PinFriendlyName=NSLOCTEXT("K2Node", "Target", "Target"),PinToolTip="Target Kismet String Library Object Reference",PinType.PinCategory="object",PinType.PinSubCategory="",PinType.PinSubCategoryObject=Class'"/Script/Engine.KismetStringLibrary"',PinType.PinSubCategoryMemberReference=(),PinType.PinValueType=(),PinType.ContainerType=None,PinType.bIsReference=False,PinType.bIsConst=False,PinType.bIsWeakPointer=False,PinType.bIsUObjectWrapper=True,DefaultObject="/Script/Engine.Default__KismetStringLibrary",PersistentGuid=00000000000000000000000000000000,bHidden=True,bNotConnectable=False,bDefaultValueIsReadOnly=False,bDefaultValueIsIgnored=False,bAdvancedView=False,bOrphanedPin=False,) CustomProperties Pin (PinId=22257AFF4730E84DE3EF0DBA7A92E1EE,PinName="A",PinToolTip="A String The original string",PinType.PinCategory="string",PinType.PinSubCategory="",PinType.PinSubCategoryObject=None,PinType.PinSubCategoryMemberReference=(),PinType.PinValueType=(),PinType.ContainerType=None,PinType.bIsReference=False,PinType.bIsConst=False,PinType.bIsWeakPointer=False,PinType.bIsUObjectWrapper=False,LinkedTo=(K2Node_CallFunction_1 DB96A96142631A1B113BC69C8B77B9BD,),PersistentGuid=00000000000000000000000000000000,bHidden=False,bNotConnectable=False,bDefaultValueIsReadOnly=False,bDefaultValueIsIgnored=False,bAdvancedView=False,bOrphanedPin=False,)