JavaFX WebView:如何更改默认光标?

huangapple go评论89阅读模式
英文:

JavaFX WebView: how do I change the default cursor?

问题

import javafx.application.Application;
import javafx.scene.Cursor;
import javafx.scene.Scene;
import javafx.scene.layout.VBox;
import javafx.scene.web.WebView;
import javafx.stage.Stage;

public class Main extends Application {
    public static void main(String[] args) {
        launch(args);
    }

    public void start(Stage primaryStage) {
        primaryStage.setTitle("JavaFX WebView Example");

        WebView webView = new WebView();
        webView.getEngine().loadContent("http://google.com");

        VBox vBox = new VBox(webView);
        Scene scene = new Scene(vBox, 960, 600);

        scene.setCursor(Cursor.CLOSED_HAND); // Doesn't work, reverted to pointer
        primaryStage.setScene(scene);
        primaryStage.show();
    }
}
英文:

How do I change WebView's default cursor? Every change I make is ignored, the icon always reverts back to the default pointer.

Example:

import javafx.application.Application;
import javafx.scene.Cursor;
import javafx.scene.Scene;
import javafx.scene.layout.VBox;
import javafx.scene.web.WebView;
import javafx.stage.Stage;

public class Main extends Application {
    public static void main(String[] args) {
        launch(args);
    }

    public void start(Stage primaryStage) {
        primaryStage.setTitle("JavaFX WebView Example");

        WebView webView = new WebView();
        webView.getEngine().loadContent("http://google.com");

        VBox vBox = new VBox(webView);
        Scene scene = new Scene(vBox, 960, 600);

        scene.setCursor(Cursor.CLOSED_HAND); // Doesn't work, reverted to pointer
        primaryStage.setScene(scene);
        primaryStage.show();
    }
}

I also tried to change the webView cursor itself, but to no avail.

答案1

得分: 4

文档的 HTML 内容定义了光标,因此您可以在加载后修改文档的正文样式:

import org.w3c.dom.Document;
import org.w3c.dom.Element;

import javafx.application.Application;
import javafx.concurrent.Worker;
import javafx.scene.Scene;
import javafx.scene.layout.VBox;
import javafx.scene.web.WebView;
import javafx.stage.Stage;

public class WebViewCursorOverride extends Application {
    public static void main(String[] args) {
        launch(args);
    }
    public void start(Stage primaryStage) {
        primaryStage.setTitle("JavaFX WebView Example");

        WebView webView = new WebView();
        webView.getEngine().getLoadWorker().stateProperty().addListener(
            (o, old, state) -> {
                if (state != Worker.State.SUCCEEDED) {
                    return;
                }

                Document doc = webView.getEngine().getDocument();
                Element body = (Element)
                    doc.getElementsByTagName("body").item(0);
                String style = body.getAttribute("style");
                body.setAttribute("style", "cursor: grab;" + style);
            });
        webView.getEngine().load("https://google.com");

        VBox vBox = new VBox(webView);
        Scene scene = new Scene(vBox, 960, 600);

        primaryStage.setScene(scene);
        primaryStage.show();
    }
}

您还可以从图像创建自己的光标:

body.setAttribute("style",
    "cursor: url('https://upload.wikimedia.org/wikipedia/commons/thumb/8/8c/Pixel_51_icon_cursor_click_top_right.svg/36px-Pixel_51_icon_cursor_click_top_right.svg.png') 27 9, default;" + style);

完整的 cursor CSS 属性定义在这里。以下是当前预定义光标的完整列表;请注意,并非所有光标在每个系统上都受支持:

  • auto
  • default
  • none
  • context-menu
  • help
  • pointer
  • progress
  • wait
  • cell
  • crosshair
  • text
  • vertical-text
  • alias
  • copy
  • move
  • no-drop
  • not-allowed
  • grab
  • grabbing
  • e-resize
  • n-resize
  • ne-resize
  • nw-resize
  • s-resize
  • se-resize
  • sw-resize
  • w-resize
  • ew-resize
  • ns-resize
  • nesw-resize
  • nwse-resize
  • col-resize
  • row-resize
  • all-scroll
  • zoom-in
  • zoom-out
英文:

The document’s HTML content defines the cursor, so you can modify the document’s body style after it loads:

import org.w3c.dom.Document;
import org.w3c.dom.Element;

import javafx.application.Application;
import javafx.concurrent.Worker;
import javafx.scene.Scene;
import javafx.scene.layout.VBox;
import javafx.scene.web.WebView;
import javafx.stage.Stage;

public class WebViewCursorOverride extends Application {
    public static void main(String[] args) {
        launch(args);
    }
    public void start(Stage primaryStage) {
        primaryStage.setTitle("JavaFX WebView Example");

        WebView webView = new WebView();
        webView.getEngine().getLoadWorker().stateProperty().addListener(
            (o, old, state) -> {
                if (state != Worker.State.SUCCEEDED) {
                    return;
                }

                Document doc = webView.getEngine().getDocument();
                Element body = (Element)
                    doc.getElementsByTagName("body").item(0);
                String style = body.getAttribute("style");
                body.setAttribute("style", "cursor: grab;" + style);
            });
        webView.getEngine().load("https://google.com");

        VBox vBox = new VBox(webView);
        Scene scene = new Scene(vBox, 960, 600);

        primaryStage.setScene(scene);
        primaryStage.show();
    }
}

You can also create your own cursor from an image:

body.setAttribute("style",
    "cursor: url('https://upload.wikimedia.org/wikipedia/commons/thumb/8/8c/Pixel_51_icon_cursor_click_top_right.svg/36px-Pixel_51_icon_cursor_click_top_right.svg.png') 27 9, default;" + style);

The full definition of the cursor CSS property is here. Here is the current list of predefined cursors; note that not all of them are supported on every system:

  • auto
  • default
  • none
  • context-menu
  • help
  • pointer
  • progress
  • wait
  • cell
  • crosshair
  • text
  • vertical-text
  • alias
  • copy
  • move
  • no-drop
  • not-allowed
  • grab
  • grabbing
  • e-resize
  • n-resize
  • ne-resize
  • nw-resize
  • s-resize
  • se-resize
  • sw-resize
  • w-resize
  • ew-resize
  • ns-resize
  • nesw-resize
  • nwse-resize
  • col-resize
  • row-resize
  • all-scroll
  • zoom-in
  • zoom-out

答案2

得分: 1

以下是您提供的代码翻译部分:

@VGR的解决方案很优雅但在我的机器上会引发闪烁问题

一个更激进的解决方案是将`WebEngine`使用的`CursorManager`替换为其他内容

例如在Java10中您可以基于默认的`CursorManagerImpl`创建这个`CursorManagerImpl2`

```java
import com.sun.webkit.CursorManager;
import com.sun.webkit.graphics.WCGraphicsManager;
import com.sun.webkit.graphics.WCImage;

import javafx.application.Application;
import javafx.scene.Cursor;
import javafx.scene.ImageCursor;
import javafx.scene.Scene;
import javafx.scene.image.Image;
import javafx.scene.layout.VBox;
import javafx.scene.web.WebView;
import javafx.stage.Stage;
import java.util.*;

public class CursorManagerImpl2 extends CursorManager<javafx.scene.Cursor> {
    // ... (略去部分代码)
}

然后您可以使用以下方式替换管理器:

CursorManager.setCursorManager(new CursorManagerImpl2(javafx.scene.Cursor.CROSSHAIR));

要更改默认光标,可以使用:

((CursorManagerImpl2) CursorManager.getCursorManager()).setDefaultCursor(javafx.scene.Cursor.MOVE);

注意,您可能需要将com.sun包添加到您的Gradle依赖中:

compileJava {
    options.compilerArgs.addAll(['--add-exports=javafx.web/com.sun.webkit=ALL-UNNAMED', '--add-exports=javafx.web/com.sun.javafx.webkit=ALL-UNNAMED', '--add-exports=javafx.web/com.sun.webkit.graphics=ALL-UNNAMED', '--add-exports=javafx.graphics/com.sun.javafx.tk=ALL-UNNAMED'])
}
run {
    jvmArgs = ['--add-exports=javafx.web/com.sun.webkit=ALL-UNNAMED', '--add-exports=javafx.web/com.sun.javafx.webkit=ALL-UNNAMED', '--add-exports=javafx.web/com.sun.webkit.graphics=ALL-UNNAMED', '--add-exports=javafx.graphics/com.sun.javafx.tk=ALL-UNNAMED']
}

通过这种方式,即使使用大型自定义光标,在移动鼠标时我不会遇到任何闪烁问题。

最大的缺点(除了com.sun的导入之外)是CursorManager是静态的;因此,所有的WebViews将使用相同的光标管理。


<details>
<summary>英文:</summary>

The solution by @VGR is elegant, but induces flickering on my machine.

A more drastic solution is replacing the `CursorManager` used by `WebEngine` by something else.

For example, in Java10 you can create this `CursorManagerImpl2` class based on the default `CursorManagerImpl`:

```java
import com.sun.webkit.CursorManager;
import com.sun.webkit.graphics.WCGraphicsManager;
import com.sun.webkit.graphics.WCImage;

import javafx.application.Application;
import javafx.scene.Cursor;
import javafx.scene.ImageCursor;
import javafx.scene.Scene;
import javafx.scene.image.Image;
import javafx.scene.layout.VBox;
import javafx.scene.web.WebView;
import javafx.stage.Stage;
import java.util.*;

public class CursorManagerImpl2 extends CursorManager&lt;javafx.scene.Cursor&gt; {
    private final Map&lt;String, javafx.scene.Cursor&gt; map = new HashMap();
    private ResourceBundle bundle;
    private javafx.scene.Cursor defaultCursor = javafx.scene.Cursor.DEFAULT;

    public CursorManagerImpl2() {}

    public CursorManagerImpl2(javafx.scene.Cursor defaultCursor) {
      this.defaultCursor = defaultCursor;
    }

    public javafx.scene.Cursor getDefaultCursor() {
      return defaultCursor;
    }

    public void setDefaultCursor(javafx.scene.Cursor cursor) {
      defaultCursor = cursor;
    }

    @Override
    protected javafx.scene.Cursor getCustomCursor(WCImage image, int hotspotX, int hotspotY) {
      return new ImageCursor(
          com.sun.javafx.tk.Toolkit.getImageAccessor()
              .fromPlatformImage(WCGraphicsManager.getGraphicsManager().toPlatformImage(image)),
          hotspotX,
          hotspotY);
    }
    @Override
    protected javafx.scene.Cursor getPredefinedCursor(int type) {
      switch (type) {
        case 0:
        default: return defaultCursor; // line changed
        case 1: return javafx.scene.Cursor.CROSSHAIR;
        case 2: return javafx.scene.Cursor.HAND;
        case 3: return javafx.scene.Cursor.MOVE;
        case 4: return javafx.scene.Cursor.TEXT;
        case 5: return javafx.scene.Cursor.WAIT;
        case 6: return this.getCustomCursor(&quot;help&quot;, javafx.scene.Cursor.DEFAULT);
        case 7: return javafx.scene.Cursor.E_RESIZE;
        case 8: return javafx.scene.Cursor.N_RESIZE;
        case 9: return javafx.scene.Cursor.NE_RESIZE;
        case 10: return javafx.scene.Cursor.NW_RESIZE;
        case 11: return javafx.scene.Cursor.S_RESIZE;
        case 12: return javafx.scene.Cursor.SE_RESIZE;
        case 13: return javafx.scene.Cursor.SW_RESIZE;
        case 14: return javafx.scene.Cursor.W_RESIZE;
        case 15: return javafx.scene.Cursor.V_RESIZE;
        case 16: return javafx.scene.Cursor.H_RESIZE;
        case 17: return this.getCustomCursor(&quot;resize.nesw&quot;, javafx.scene.Cursor.DEFAULT);
        case 18: return this.getCustomCursor(&quot;resize.nwse&quot;, javafx.scene.Cursor.DEFAULT);
        case 19: return this.getCustomCursor(&quot;resize.column&quot;, javafx.scene.Cursor.H_RESIZE);
        case 20: return this.getCustomCursor(&quot;resize.row&quot;, javafx.scene.Cursor.V_RESIZE);
        case 21: return this.getCustomCursor(&quot;panning.middle&quot;, javafx.scene.Cursor.DEFAULT);
        case 22: return this.getCustomCursor(&quot;panning.east&quot;, javafx.scene.Cursor.DEFAULT);
        case 23: return this.getCustomCursor(&quot;panning.north&quot;, javafx.scene.Cursor.DEFAULT);
        case 24: return this.getCustomCursor(&quot;panning.ne&quot;, javafx.scene.Cursor.DEFAULT);
        case 25: return this.getCustomCursor(&quot;panning.nw&quot;, javafx.scene.Cursor.DEFAULT);
        case 26: return this.getCustomCursor(&quot;panning.south&quot;, javafx.scene.Cursor.DEFAULT);
        case 27: return this.getCustomCursor(&quot;panning.se&quot;, javafx.scene.Cursor.DEFAULT);
        case 28: return this.getCustomCursor(&quot;panning.sw&quot;, javafx.scene.Cursor.DEFAULT);
        case 29: return this.getCustomCursor(&quot;panning.west&quot;, javafx.scene.Cursor.DEFAULT);
        case 30: return this.getCustomCursor(&quot;vertical.text&quot;, javafx.scene.Cursor.DEFAULT);
        case 31: return this.getCustomCursor(&quot;cell&quot;, javafx.scene.Cursor.DEFAULT);
        case 32: return this.getCustomCursor(&quot;context.menu&quot;, javafx.scene.Cursor.DEFAULT);
        case 33: return this.getCustomCursor(&quot;no.drop&quot;, javafx.scene.Cursor.DEFAULT);
        case 34: return this.getCustomCursor(&quot;not.allowed&quot;, javafx.scene.Cursor.DEFAULT);
        case 35: return this.getCustomCursor(&quot;progress&quot;, javafx.scene.Cursor.WAIT);
        case 36: return this.getCustomCursor(&quot;alias&quot;, javafx.scene.Cursor.DEFAULT);
        case 37: return this.getCustomCursor(&quot;zoom.in&quot;, javafx.scene.Cursor.DEFAULT);
        case 38: return this.getCustomCursor(&quot;zoom.out&quot;, javafx.scene.Cursor.DEFAULT);
        case 39: return this.getCustomCursor(&quot;copy&quot;, javafx.scene.Cursor.DEFAULT);
        case 40: return javafx.scene.Cursor.NONE;
        case 41: return this.getCustomCursor(&quot;grab&quot;, javafx.scene.Cursor.OPEN_HAND);
        case 42: return this.getCustomCursor(&quot;grabbing&quot;, javafx.scene.Cursor.CLOSED_HAND);
      }
    }

    private javafx.scene.Cursor getCustomCursor(String cursorId, javafx.scene.Cursor defaultCursor) {
      javafx.scene.Cursor customCursor = this.map.get(cursorId);
      if (customCursor == null) {
        try {
          if (this.bundle == null) {
            this.bundle =
                ResourceBundle.getBundle(&quot;com.sun.javafx.webkit.Cursors&quot;, Locale.getDefault());
          }

          if (this.bundle != null) {
            String fileName = this.bundle.getString(cursorId + &quot;.file&quot;);
            javafx.scene.image.Image image =
                new Image(com.sun.javafx.webkit.CursorManagerImpl.class.getResourceAsStream(fileName));
            fileName = this.bundle.getString(cursorId + &quot;.hotspotX&quot;);
            int hotspotX = Integer.parseInt(fileName);
            fileName = this.bundle.getString(cursorId + &quot;.hotspotY&quot;);
            int hotspotY = Integer.parseInt(fileName);
            customCursor = new ImageCursor(image, hotspotX, hotspotY);
          }
        } catch (MissingResourceException e) {
        }

        if (customCursor == null) {
          customCursor = defaultCursor;
        }

        this.map.put(cursorId, customCursor);
      }

      return customCursor;
    }
} 

You can then replace the manager with

CursorManager.setCursorManager(new CursorManagerImpl2(javafx.scene.Cursor.CROSSHAIR));

To change the default cursor, you can use

((CursorManagerImpl2) CursorManager.getCursorManager()).setDefaultCursor(javafx.scene.Cursor.MOVE);

Note that you might have to add the com.sun packages to your gradle:

compileJava {
    options.compilerArgs.addAll([&#39;--add-exports=javafx.web/com.sun.webkit=ALL-UNNAMED&#39;, &#39;--add-exports=javafx.web/com.sun.javafx.webkit=ALL-UNNAMED&#39;, &#39;--add-exports=javafx.web/com.sun.webkit.graphics=ALL-UNNAMED&#39;, &#39;--add-exports=javafx.graphics/com.sun.javafx.tk=ALL-UNNAMED&#39;])
}
run {
    jvmArgs = [&#39;--add-exports=javafx.web/com.sun.webkit=ALL-UNNAMED&#39;, &#39;--add-exports=javafx.web/com.sun.javafx.webkit=ALL-UNNAMED&#39;, &#39;--add-exports=javafx.web/com.sun.webkit.graphics=ALL-UNNAMED&#39;, &#39;--add-exports=javafx.graphics/com.sun.javafx.tk=ALL-UNNAMED&#39;]
}

With this, I do not suffer from any flickering while moving the mouse, even when using large custom cursors.

The big downside (beside the com.sun imports) is that the CursorManager is static; as such, all WebViews will use the same cursor management.

huangapple
  • 本文由 发表于 2020年4月4日 07:24:47
  • 转载请务必保留本文链接:https://go.coder-hub.com/61021849.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定