【问题标题】:LibGDX html clipboardLibGDX html剪贴板
【发布时间】:2016-04-29 14:41:54
【问题描述】:

我在 LibGDX 中开发游戏,游戏中有登录界面和注册界面。 HTML版本的游戏有剪贴板的沙盒环境,意思是:

从游戏中复制的任何内容,不能粘贴到游戏外& 从外部复制的任何内容都不能粘贴到游戏的文本字段中

我只是想复制文本,有什么方法可以将沙盒剪贴板与系统剪贴板合并?

我想要的是: 当用户在文本字段中执行 Ctr+V 时,它应该从文本字段中的系统剪贴板中抓取文本,当用户按下 Ctr+C 时:它应该将文本放入系统剪贴板中

我正在尝试什么:

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");
 }
}

谁能帮帮我:

  • 如何将参数抓取到事件(抓取复制的文本)
  • 只有在 DOM 中发生复制事件时才会触发,我如何获取系统剪贴板

编辑(5 月 2 日,尝试过 JustACluelessNewbie 的建议): 继承剪贴板:

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);
         }
     }-*/ ;
}
  • 我在最新的 chrome 上运行,它说它支持粘贴命令,但是 不粘贴
  • 它被复制到系统剪贴板,我可以在剪贴板中看到,但它 按 Ctr+V 或右键粘贴时不粘贴

【问题讨论】:

    标签: javascript gwt libgdx clipboard


    【解决方案1】:

    终于搞定了,如果有人还在找这个,我会发布我的答案

    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 self.@myPackage.HtmlLauncher::copy()();
            });
            var pasteText = $entry(function(text){
                self.@myPackage.HtmlLauncher::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();
        }
    }
    

    我为所有屏幕创建了一个超类,这个问题所需的最小结构:

    public abstract class BasicScreen extends ScreenAdapter implements InputProcessor {
        protected MyStage stage;
    
        public BasicScreen () {
            stage = new MyStage();
        }
    
         public MyStage getStage () {
            return stage;
         }
    }
    

    你需要在同一阶段添加文本字段

    在 index.html 中,添加以下 2 行:

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

    在 css 中添加:

    .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 */
    }
    

    它在 chrome 中完美运行,没有在其他浏览器上测试。

    我参考了: https://www.lucidchart.com/techblog/2014/12/02/definitive-guide-copying-pasting-javascript/

    【讨论】:

      【解决方案2】:

      你是对的 - GWT 剪贴板甚至不尝试访问“全局”剪贴板,仅在应用程序本身内复制和粘贴数据。您可以尝试将GwtClipboard 替换为使用本机代码访问剪贴板数据的实现。你可以尝试转换Clipboard实现实验,未完成Dragome backend:你可以找到它here。基本上是这样的:

      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);
          }
      }
      

      我认为它未经测试,但绝对值得一试。请记住,大多数浏览器将不允许访问剪贴板,除非它是由用户输入事件进行的;由于复制很可能由本机事件侦听器直接处理(事件被轮询并稍后处理),因此它可能无法按预期工作。

      您可以覆盖 GwtApplication#getClipboard() 并返回转换后的实现 - 如果这样做,它应该被所有 LibGDX 实用程序使用,例如 Scene2D 小部件。

      【讨论】:

      • 我猜浏览器不允许粘贴命令,我没有使用他的确切代码,而是使用本机方法执行 javascript
      • 这就是我提到的——复制和粘贴只能在用户事件之后直接执行。例如,您可以将本机事件侦听器连接到 document,然后尝试从那里开始。
      • 是的,我明白了,我会在周末尝试更多并更新有关新发现的帖子。非常感谢
      猜你喜欢
      • 1970-01-01
      • 2013-01-25
      • 2011-02-16
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2023-02-09
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多