Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

LibGDX html clipboard

I'm developing game in LibGDX, there are login screen and registration screen in game. HTML version of game has its sandbox environment for clipboard, means:

Anything copied from game, can not be pasted outside game & anything copied from outside can not be pasted in textfield of game

I'm just trying to copy text, Is there any way I can merge sandbox clipboard with system clipboard?

What I want is: when user does Ctr+V in textfield, it should grab text from system clipboard in textfield & when user press Ctr+C: it should put text in system clipboard

What I'm trying:

public class HtmlLauncher extends GwtApplication {
 private static HtmlLauncher instance;
 public void onModuleLoad() {
  instance = this;
  setLoadingListener(new LoadingListener() {
   @Override
   public void beforeSetup() {}

   @Override
   public void afterSetup() {
    setupCopyListener();
   }
  });
 }
 native void setupCopyListener()
  /*-{
         var htmlLauncher_onCopy = $entry(@com.myapp.game.client.HtmlLauncher::addToClipboard());
         $wnd.addEventListener("copy", htmlLauncher_onCopy, false);
     }-*/
 ;

 public static void addToClipboard() {
  instance.copy();
 }

 private void copy() {
  //getClipboard().setContents("");
  consoleLog("copied");
 }
}

Can anyone help me with:

  • how to grab parameter to event (to grab text copied)
  • This gets fired only when copy event occurs in DOM, how do I get system clipboard

edit (May 2, tried suggestion of JustACluelessNewbie): inheriting Clipboard:

public class MyClipboard implements com.badlogic.gdx.utils.Clipboard{
 private String cachedContent = "";

 public MyClipboard() {
  createTextArea();
 }

 @Override
 public String getContents() {
  String contents = getClipBoard();
  return (contents == null) ? cachedContent : cachedContent = contents;
 }

 @Override
 public void setContents(String content) {
  cachedContent = content == null ? "" : content;
  setClipBoard(content);
 }

 public static native void createTextArea() /*-{
         var textArea = document.createElement('textarea');
         textArea.style.position='fixed';
         textArea.style.top=0;
         textArea.style.left=0;
         textArea.style.width='2em';
         textArea.style.height='2em';
         textArea.style.padding=0;
         textArea.style.border='none';
         textArea.style.outline='none';
         textArea.style.boxShadow='none';
         textArea.style.background='transparent';
         $wnd._copy=textArea;
     }-*/;

 public static native String getClipBoard() /*-{
         if(window.clipboardData){
             return window.clipboardData.getData('Text');
         }else {
             document.body.appendChild($wnd._copy);
             try{
                 $wnd._copy.value = "";
                 $wnd._copy.focus();
                 $wnd._copy.select();
                 console.log(document.queryCommandSupported("paste")); //prints true
                 var result = document.execCommand('paste');
                 console.log(result);  //prints false
                 return $wnd._copy.value;
             }catch(err){
                 return null;
             }finally{
                 document.body.removeChild($wnd._copy);
             }
         }
     }-*/;

 public static native void setClipBoard(String content)  /*-{
         document.body.appendChild($wnd._copy);
         try{
             $wnd._copy.value = content;
             $wnd._copy.select();
             var result = document.execCommand('copy');
             console.log("after exec copy "+result)
         }catch(err){
             console.log("error:"+err);
         }finally{
             document.body.removeChild($wnd._copy);
         }
     }-*/ ;
}
  • I'm running on latest chrome, it says it supports paste command but doesn't paste
  • It gets copied to system clipboard, I can see in clipboard but it doesn't paste on pressing Ctr+V or right click paste
like image 726
sagar Avatar asked Feb 11 '26 15:02

sagar


2 Answers

Finally I got it working, I'm posting my answer if anyone is still looking for this

package myPackage;

public class HtmlLauncher extends GwtApplication {
    private static HtmlLauncher instance;

    @Override
    public void onModuleLoad() {
        super.onModuleLoad();
        instance = this;
        setLoadingListener(new LoadingListener() {
            @Override
            public void beforeSetup() {
            }

            @Override
            public void afterSetup() {
                setupCopyListener();
            }
        });
    }

    native void setupCopyListener() /*-{
        var self = this;
        var isSafari = navigator.appVersion.search('Safari') != -1 && navigator.appVersion.search('Chrome') == -1 && navigator.appVersion.search('CrMo') == -1 && navigator.appVersion.search('CriOS') == -1;
        var isIe = (navigator.userAgent.toLowerCase().indexOf("msie") != -1 || navigator.userAgent.toLowerCase().indexOf("trident") != -1);

        var ieClipboardDiv = $doc.getElementById('#ie-clipboard-contenteditable');
        var hiddenInput = $doc.getElementById("hidden-input");
        var getTextToCopy = $entry(function(){
            return [email protected]::copy()();
        });
        var pasteText = $entry(function(text){
            [email protected]::paste(Ljava/lang/String;)(text);
        });

        var focusHiddenArea = function() {
            // In order to ensure that the browser will fire clipboard events, we always need to have something selected
            hiddenInput.value = '';
            hiddenInput.focus();
            hiddenInput.select();
        };

        // Focuses an element to be ready for copy/paste (used exclusively for IE)
        var focusIeClipboardDiv = function() {
            ieClipboardDiv.focus();
            var range = document.createRange();
            range.selectNodeContents((ieClipboardDiv.get(0)));
            var selection = window.getSelection();
            selection.removeAllRanges();
            selection.addRange(range);
        };

        // For IE, we can get/set Text or URL just as we normally would,
        // but to get HTML, we need to let the browser perform the copy or paste
        // in a contenteditable div.
        var ieClipboardEvent = function(clipboardEvent) {
            var clipboardData = window.clipboardData;
            if (clipboardEvent == 'cut' || clipboardEvent == 'copy') {
                clipboardData.setData('Text', getTextToCopy());
                focusIeClipboardDiv();
                setTimeout(function() {
                    focusHiddenArea();
                    ieClipboardDiv.empty();
                }, 0);
            }
            if (clipboardEvent == 'paste') {
                var clipboardText = clipboardData.getData('Text');
                ieClipboardDiv.empty();
                setTimeout(function() {
                    pasteText(clipboardText);
                    ieClipboardDiv.empty();
                    focusHiddenArea();
                }, 0);
            }
        };

        // For every broswer except IE, we can easily get and set data on the clipboard
        var standardClipboardEvent = function(clipboardEvent, event) {
            var clipboardData = event.clipboardData;
            if (clipboardEvent == 'cut' || clipboardEvent == 'copy') {
                clipboardData.setData('text/plain', getTextToCopy());
            }
            if (clipboardEvent == 'paste') {
                pasteText(clipboardData.getData('text/plain'));
            }
        };

        ['cut', 'copy', 'paste'].forEach(function (event) {
            $doc.addEventListener(event,function (e) {
                console.log(event);
                if(isIe) {
                    ieClipboardEvent(event);
                } else {
                    standardClipboardEvent(event, e);
                    focusHiddenArea();
                    e.preventDefault();
                }
            })
        })
    }-*/;

    private void paste(String text) {
        consoleLog("in paste"+text);
        String oldText = getClipboard().getContents();
        if(!oldText.equals(text)){
            getClipboard().setContents(text);
            Actor focusedActor = ((BasicScreen)((Game)getApplicationListener()).getScreen()).getStage().getKeyboardFocus();
            if (focusedActor != null && focusedActor instanceof TextField) {
                if(!oldText.equals("")) {
                    String textFieldText = ((TextField)focusedActor).getText();
                    textFieldText = textFieldText.substring(0,textFieldText.lastIndexOf(oldText));
                    ((TextField)focusedActor).setText(textFieldText);
                }
                ((TextField)focusedActor).appendText(text);
            }
        }
    }

    private String copy() {
        return getClipboard().getContents();
    }
}

I have created one super class for all screens, minimum structure required for this problem:

public abstract class BasicScreen extends ScreenAdapter implements InputProcessor {
    protected MyStage stage;

    public BasicScreen () {
        stage = new MyStage();
    }

     public MyStage getStage () {
        return stage;
     }
}

you need to add textfield in same stage

in index.html, add this 2 line inside:

<div id="ie-clipboard-contenteditable" class="hidden" contenteditable="true"></div>
<input id="hidden-input" class="hidden" type="text" value=""/>

in css, add this:

.hidden {
    position: fixed;
    bottom: 0;
    left: 0;
    width: 10px;
    height: 10px;
    display: block;
    font-size: 1;
    z-index: -1;
    color: transparent;
    background: transparent;
    overflow: hidden;
    border: none;
    padding: 0;
    resize: none;
    outline: none;
    -webkit-user-select: text;
    user-select: text;
    /* Because for user-select:none, Safari won't allow input */
}

It works perfectly in chrome, did't test on other browsers.

I took reference from: https://www.lucidchart.com/techblog/2014/12/02/definitive-guide-copying-pasting-javascript/

like image 148
sagar Avatar answered Feb 13 '26 13:02

sagar


You're right - GWT clipboard doesn't even try accessing the "global" clipboard, copying and pasting data only within the application itself. You could try replacing the GwtClipboard with your implementation that uses native code to access the clipboard data. You can try converting Clipboard implementation experimental, unfinished Dragome backend: you can find it here. It basically looks like this:

public class DragomeClipboard implements Clipboard {
    private String cachedContent = "";

    public DragomeClipboard () {
        ScriptHelper.evalNoResult(
            "var textArea=document.createElement('textarea');textArea.style.position='fixed';textArea.style.top=0;textArea.style.left=0;textArea.style.width='2em';textArea.style.height='2em';textArea.style.padding=0;textArea.style.border='none';textArea.style.outline='none';textArea.style.boxShadow='none';textArea.style.background='transparent';this._copy=textArea;",
            this);
    }

    @Override
    public String getContents () {
        try {
            ScriptHelper.put("_cache", cachedContent, this);
            final String content = String.valueOf(ScriptHelper.eval(
                "if(window.clipboardData){return window.clipboardData.getData('Text');}else{document.body.appendChild(this._copy);try{this._copy.select();document.execCommand('paste');return this._copy.value;}catch(err){return _cache;}finally{document.body.removeChild(this._copy;}}",
                this));
            cachedContent = content;
            return content;
        } catch (final Throwable exception) {
            Exceptions.ignore(exception);
            return cachedContent;
        }
    }

    @Override
    public void setContents (final String content) {
        cachedContent = content == null ? "" : content;
        ScriptHelper.evalNoResult(
            "document.body.appendChild(this._copy);try{this._copy.select();document.execCommand('copy');}catch(err){}finally{document.body.removeChild(this._copy);}",
            this);
    }
}

I think it's untested, but it's definitely worth a try. Keep in mind that most browsers will disallow accessing clipboard, unless it is proceeded by a user input event; since copying is most likely not handled directly by a native event listener (events are polled and handled later), it might not work as expected.

You can override GwtApplication#getClipboard() and return your converted implementation - if you do so, it should be used by all LibGDX utilities, like Scene2D widgets.

like image 29
Czyzby Avatar answered Feb 13 '26 14:02

Czyzby



Donate For Us

If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!