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:
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);
}
}-*/ ;
}
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/
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.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With