Indie game storeFree gamesFun gamesHorror games
Game developmentAssetsComics
SalesBundles
Jobs
Tags
0
Members

[tip] Getting clipboard data!

A topic by Doomcaster created Feb 11, 2016 Views: 1,234 Replies: 2
Viewing posts 1 to 4
(2 edits) (+3)

(I still don't know if this qualifies as a guide or not, haha)

At first getting clipboard data seemed difficult due to Electron (what Superpowers is running on) not supporting window.prompt, and the fact you couldn't access the clipboard directly due to security risks. (like stealing user info, etc...). But after Élisée pointed me in the right direction, I was able to come up with a few different solutions which eventually turned into the code below!

The way I was able to get around this was to create an input box above the game to receive the "onpaste" event. While this worked well, it didn't look very good. It moved around if the window resolution was changed and looked a bit out of place. I decided to clean up a lot of the messy code I had and ended up rewriting the clipboard system to be pretty easy to use, so I figured I'd share it with the community. :D


First you'll need to add this somewhere in a script. Just make sure you don't put it in a behavior or it might not work properly.

declare var document;
let canvas = document.getElementsByTagName("canvas")[0];

This allows access to the document (the page the game is on) and the canvas (what the game is in). This might not be the best way to get the canvas, but it was the only way I knew how to get to it.


This should be added into it's own empty script to avoid cluttering up existing code in your project.

namespace Clipboard {
    // Create a hidden input.
    let content = document.body.appendChild(document.createElement("INPUT"));
    content.setAttribute("type", "text");
    content.style.cssText = "position:absolute;width:1px;height:1px;left:0px;z-index:-1;visibility:none;";

    // Redirect the paste event to "content".
    document.onpaste = (event) => {
        content.value = "";
        content.focus();
        Sup.setTimeout(4, () => {
            canvas.focus();
        });
    };

    // Get the previous data in "content".
    export function getData(): string {
        return(content.value);
    }

    // Clear the data in "content".
    export function clear(): void {
        content.value = "";
    }

    // Continually check for content until the breakpoint is reached, or time has run out.
    function waitFor(callback: (data) => void, breakpoint: () => Boolean): void
    function waitFor(callback: (data) => void, timeout: number): void
    function waitFor(callback: (data) => void, exit: any): void {
        if((content.value!="")||((typeof exit == "number") ? exit<0 : exit())) {
            callback(content.value);
        } else {
            Sup.setTimeout(4, () => { waitFor(callback, (typeof exit == "number") ? exit-1 : exit); });
        }
    }

    // The "initialization" function to wait for input.
    export function onPaste(callback: (data) => void, breakpoint: Function): void
    export function onPaste(callback: (data) => void, timeout: number): void
    export function onPaste(callback: (data) => void, exit: any): void {
        content.value = "";
        waitFor(callback, exit);
    }
}

This will create a hidden input that will grab whatever you paste into the game using Ctrl+V, without the game losing focus. I added Clipboard.clear() and Clipboard.getData() for those who want to manually check for the content themselves, but I recommend using Clipboard.onPaste() if you want a more automated system with a bit more flexibility.


Using Clipboard.onPaste() is a bit complex, but it's pretty useful. It's a recursive function, which means it'll run in the background until it either gets something from the clipboard, the "breakpoint" tells it to stop, or it runs out of time. You'll need to make sure there's a way the breakpoint will exit or a timeout is set. Otherwise this will continue running until something is pasted which might cause lag, or other problems. So if the player decides not to paste anything and clicks a "cancel" button, be sure to make the breakpoint check if that button was pressed or removed from the scene!

callback:

Type: Function
This argument requires a function with a single argument to receive the content of the clipboard. Once something has been pasted, it'll call this function. Check out how to use anonymous functions in the TypeScript Primer, it's pretty useful!

breakpoint: (method 1)

Type: Function
This argument also requires a function, but it must return a Boolean value (true or false). While waiting for something to be pasted, it will continually run this function to check if it should stop. Upon returning True, this will quit waiting for input. This could be as simple as checking if "ESCAPE" was pressed. Again, anonymous functions are very useful for this :D

timeout: (method 2)

Type: number (in milliseconds)
If a timeout is specified instead of a breakpoint, it will countdown until the timeout reaches 0 and give up. This may not be useful for much, but I added it just so the option was there.
Example:
// Breakpoint example.
Clipboard.onPaste(
    (data) => { Sup.log(data); },
    () => { return(Sup.Input.wasKeyJustPressed("ESCAPE")); }
);

// Timeout example. (30 seconds)
Clipboard.onPaste(
    (data) => { Sup.log(data); },
    30000
);

Whether the timeout runs out or a breakpoint was reached, it will call the "callback" function and return an empty string.


Using Clipboard.get() and Clipboard.clear() are pretty self-explanatory, just clear the the clipboard to make sure it's empty, then just keep checking Clipboard.get() until it returns a value. You can also ignore Clipboard.waitFor() because you can't normally access it. I did this so Clipboard.onPaste() could clear the clipboard before waiting for content so that it's not accidentally triggered. (and you won't have to call Clipboard.clear() just to use Clipboard.onPaste()...)

While this doesn't cover context menus (right-click > paste) it's as close as I could get to clipboard support while keeping it relatively clean and simple. Hopefully I was able to explain Clipboard.onPaste() clearly enough, since it might be a little difficult to understand at first. Hope this helps!

Well made! This is good insight, thank you for sharing.

Hi I was wondering if this is still up to date? If so, Doomcaster, could  you give a full example on how to use this? If I want in a section of my program to be able to access the clipboard content, I use all the  code you wrote above:

declare var document;

etc. and

namespace Clipboard {

etc. and what else, if what I want is to have one variable that stores the clipboard content?

var clipboardcontent = ?