diff --git a/src/js/core/config.local.template.js b/src/js/core/config.local.template.js index 5e3cdad6..0430753e 100644 --- a/src/js/core/config.local.template.js +++ b/src/js/core/config.local.template.js @@ -113,5 +113,8 @@ export default { // Disables slow asserts, useful for debugging performance // disableSlowAsserts: true, // ----------------------------------------------------------------------------------- + // Enable copy and paste using system clipboard + // useClipboard: true, + // ----------------------------------------------------------------------------------- /* dev:end */ }; diff --git a/src/js/game/hud/parts/blueprint_placer.js b/src/js/game/hud/parts/blueprint_placer.js index be831928..b9553295 100644 --- a/src/js/game/hud/parts/blueprint_placer.js +++ b/src/js/game/hud/parts/blueprint_placer.js @@ -13,6 +13,9 @@ import { KEYMAPPINGS } from "../../key_action_mapper"; import { BaseHUDPart } from "../base_hud_part"; import { Entity } from "../../entity"; import { DynamicDomAttach } from "../dynamic_dom_attach"; +import { globalConfig } from "../../../core/config"; + +const copy = require("clipboard-copy"); const logger = createLogger("blueprint_placer"); @@ -55,9 +58,6 @@ export class HUDBlueprintPlacer extends BaseHUDPart { this.trackedCanAfford = new TrackedState(this.onCanAffordChanged, this); this.serializer = new SerializerInternal(); - - // TODO: This probably belongs at a higher level - document.addEventListener("paste", this.pasteFromClipboard.bind(this)); } abortPlacement() { @@ -159,6 +159,8 @@ export class HUDBlueprintPlacer extends BaseHUDPart { return; } this.currentBlueprint.set(Blueprint.fromUids(this.root, uids)); + + this.copyToClipboard(); } /** @@ -177,16 +179,22 @@ export class HUDBlueprintPlacer extends BaseHUDPart { /** * Attempts to paste the last blueprint */ - pasteBlueprint() { - if (this.lastBlueprintUsed !== null) { - if (this.lastBlueprintUsed.layer !== this.root.currentLayer) { + async pasteBlueprint() { + /** @type {Blueprint|void} */ + let blueprint = null; + if (G_IS_DEV && globalConfig.debug.useClipboard) { + blueprint = await this.pasteFromClipboard(); + } + blueprint = blueprint || this.lastBlueprintUsed; + if (blueprint !== null) { + if (blueprint.layer !== this.root.currentLayer) { // Not compatible this.root.soundProxy.playUiError(); return; } this.root.hud.signals.pasteBlueprintRequested.dispatch(); - this.currentBlueprint.set(this.lastBlueprintUsed); + this.currentBlueprint.set(blueprint); } else { this.root.soundProxy.playUiError(); } @@ -212,56 +220,82 @@ export class HUDBlueprintPlacer extends BaseHUDPart { } /** - * @param {ClipboardEvent} event + * Copy blueprint to system clipboard */ - pasteFromClipboard(event) { - const data = event.clipboardData.getData("Text"); - if (!data) { + async copyToClipboard() { + if (!G_IS_DEV || !globalConfig.debug.useClipboard) { return; } - let json; + const blueprint = this.currentBlueprint.get(); + // TODO: Move serialize() to Blueprint? + const serializedBP = this.serializer.serializeEntityArray(blueprint.getEntities()); + const json = JSON.stringify(serializedBP); + try { - json = JSON.parse(data); - } catch (error) { - logger.error("Unable to parse clipboard data:", error.message); - return; + await copy(json); + this.root.soundProxy.playUi(SOUNDS.copy); + logger.debug("Copied blueprint to clipboard"); + } catch (e) { + logger.error("Copy to clipboard failed:", e.message); } - if (!verifyBlueprintData(json)) { - logger.error("Invalid clipboard data"); - return; + } + + /** + * Attempt to get Blueprint from clipboard + * @returns {Promise} + */ + async pasteFromClipboard() { + let data; + try { + data = await paste(); + // logger.debug("Paste data:", data); + } catch (e) { + logger.error("Problems with paste:", e.message); } - logger.log("Paste blueprint from clipboard"); - const entityArray = []; - for (let i = 0; i < json.length; ++i) { - const serializedEntity = json[i]; - const result = this.serializer.deserializeEntity(this.root, serializedEntity); - if (typeof result == "string") { - logger.error(result); + return this.deserializeBlueprint(data); + } + + /** + * @param {string} data + * @retruns {Blueprint|void} + */ + deserializeBlueprint(data) { + // TODO: Move to Blueprint? + try { + let json = JSON.parse(data); + if (typeof json != "object") { return; } - entityArray.push(result); + if (!Array.isArray(json)) { + return; + } + /** @type {Array} */ + const entityArray = []; + for (let i = 0; i < json.length; ++i) { + /** @type {Entity?} */ + const value = json[i]; + if (value.components == undefined || value.components.StaticMapEntity == undefined) { + return; + } + const result = this.serializer.deserializeEntity(this.root, value); + if (typeof result === "string") { + throw new Error(result); + } + entityArray.push(result); + } + const newBP = new Blueprint(entityArray); + return newBP; + } catch (e) { + logger.error("Invalid bluerint data:", e.message); } - const newBP = new Blueprint(entityArray); - this.currentBlueprint.set(newBP); } } +/* TODO: Move this to a module */ /** - * Verify data is a valid serialized blueprint - * @param {Array} data + * @returns {Promise} */ -function verifyBlueprintData(data) { - if (typeof data != "object") { - return false; - } - if (!Array.isArray(data)) { - return false; - } - for (let i = 0; i < data.length; ++i) { - const value = data[i]; - if (value.components == undefined || value.components.StaticMapEntity == undefined) { - return false; - } - } - return true; +async function paste() { + /* TODO: Add fallback method */ + return navigator.clipboard.readText(); } diff --git a/src/js/game/hud/parts/mass_selector.js b/src/js/game/hud/parts/mass_selector.js index 4845a09d..33cb18f9 100644 --- a/src/js/game/hud/parts/mass_selector.js +++ b/src/js/game/hud/parts/mass_selector.js @@ -45,9 +45,6 @@ export class HUDMassSelector extends BaseHUDPart { this.root.signals.editModeChanged.add(this.clearSelection, this); this.serializer = new SerializerInternal(); - - // TODO: This probably belongs at a higher level - document.addEventListener("copy", this.copyToClipboard.bind(this)); } /** @@ -208,24 +205,6 @@ export class HUDMassSelector extends BaseHUDPart { } } - /** - * @param {ClipboardEvent} event - */ - copyToClipboard(event) { - if (this.selectedUids.size == 0) { - return; - } - - const entityUids = Array.from(this.selectedUids); - const blueprint = Blueprint.fromUids(this.root, entityUids); - const serializedBP = this.serializer.serializeEntityArray(blueprint.getEntities()); - const json = JSON.stringify(serializedBP); - event.clipboardData.setData("text/plain", json); - event.preventDefault(); - - logger.log("Copied selection to clipboard"); - } - /** * mouse down pre handler * @param {Vector} pos diff --git a/src/js/savegame/serializer_internal.js b/src/js/savegame/serializer_internal.js index 322bb2e5..fd564e5d 100644 --- a/src/js/savegame/serializer_internal.js +++ b/src/js/savegame/serializer_internal.js @@ -33,7 +33,7 @@ export class SerializerInternal { for (let i = 0; i < array.length; ++i) { const serializedEntity = array[i]; const result = this.deserializeEntity(root, serializedEntity); - if (typeof result == "string") { + if (typeof result === "string") { return result; } result.uid = serializedEntity.uid; @@ -64,7 +64,7 @@ export class SerializerInternal { rotationVariant: data.rotationVariant, variant: data.variant, }); - + const errorStatus = this.deserializeComponents(root, entity, payload.components); return errorStatus || entity;