This discussion is archived
14 Replies Latest reply: Aug 16, 2013 5:24 PM by 963782 RSS

How to realize the functionality like the JLayer in Swing?

963782 Newbie
Currently Being Moderated

JLayer in Swing is a decorator for Swing components. It enables us to draw on components and respond to component events without modifying the underlying component directly, which is interesting  to me. How to do that in JavaFX?

  • 1. Re: How to realize the functionality like the JLayer in Swing?
    jsmith Guru
    Currently Being Moderated

    Technique Description - Use a StackPane

     

    For simple layering, wrap the underlying node in a StackPane and add further nodes to the stackpane to layer them on top.

     

    One technique is to just use two nodes in the StackPane, the original node and a Pane on top the original node.  Because the Pane is a automatically resizable Node, the Pane size will adjust to completely cover the Node below. By setting the pickOnBounds and mouseTransparent properties of the Pane appropriately, you can decide whether you want to top pane to consume all mouse events, consume only mouse events on the areas where the top Pane is not transparent and let the rest pass through to the underlying node or let all mouse events be handled by the underlying node.  The top pane is sometimes known as a glass pane.

     

    Links to examples which use the described technique

     

    A very simple example with no input processing is =>

    javafx 2 - How to implement a transparent Pane with non-transparent children? - Stack Overflow

     

    A more comprehensive demonstration of the technique is han solo's blog post on using a glass pane for processing =>

    http://harmoniccode.blogspot.com/2013/04/validate-it.html

     

    For a supported library by a commercial company see the Decoration Utilities from JideFX =>

    Highlights of the JideFX Beta Release (2 of 3) | David Qiao's Blog

     

    Suggested Approach

     

    If you just have one-off requirements than rolling your own like in the StackOverflow answer or han solo's blog is the way to go.

    If you have more sophisticated requirements, you should look into JideFX (it is an open source library).

     

    And I hope I've got this answer right because I've never used JLayer in Swing, but it appears similar to what I have described.

  • 2. Re: How to realize the functionality like the JLayer in Swing?
    963782 Newbie
    Currently Being Moderated

    Thanks for your replay. Firstly, Harmonic Code: Validate it... for processing mouse events is not a valid link for me Secondly, I understand that by setting mouseTransparent to true, will let all mouse events be handled by the underlying node; by setting it to false, will let the top pane to consume all mouse events. However, consume only mouse events on the areas where the top Pane is not transparent and let the rest pass through to the underlying node confuse me. I can not achieve that. Is that related to pickOnBounds?

  • 3. Re: How to realize the functionality like the JLayer in Swing?
    jsmith Guru
    Currently Being Moderated

    > Harmonic Code: Validate it... for processing mouse events is not a valid link for me

     

    That's weird the link works fine for me as you posted it in your reply.

     

    > consume only mouse events on the areas where the top Pane is not transparent and let the rest pass through to the underlying node confuse me. I can not achieve that. Is that related to pickOnBounds?

     

    Yes.

  • 4. Re: How to realize the functionality like the JLayer in Swing?
    jsmith Guru
    Currently Being Moderated

    See also sample code:

    https://gist.github.com/jewelsea/4601070 "Demonstrates the JavaFX node mouseTransparent and pickOnBounds properties."

     

    And the related forum question:

    Best approach to manage overlay node?

  • 5. Re: How to realize the functionality like the JLayer in Swing?
    963782 Newbie
    Currently Being Moderated

    Thanks for the link. I can view that. I understand what pickOnBounds matters now. However, consider what I want to do:

    Button addButton = new Button("Add");
    /* set the shape of button to be circle */
    addButton.setStyle("-fx-shape: \"m 307.14285,366.64789 c 27.7,0 50,22.3 50,50 0,27.7 -22.3,50 -50,50 -27.7,0 -50,-22.3 -50,-50 0,-27.7 22.3,-50 50,-50 z\";");
    addButton.prefHeightProperty().bind(addButton.widthProperty());
    

    Regardless of "addButton.setPickOnBounds(false);" or "addButton.setPickOnBounds(true);", the mouse event responses always in an entire rectangular area of button. How to achieve that mouse events react only on the geometry bounds of SVG shape specified?

  • 6. Re: How to realize the functionality like the JLayer in Swing?
    jsmith Guru
    Currently Being Moderated

    Looks like pickOnBounds doesn't work with nodes (or at least buttons) which have a -fx-shape style specified.

     

    I suggest you log a bug regarding this.

     

    Here is a simple test case you can include in your bug report:

     

    import javafx.application.Application;
    import javafx.scene.Scene;
    import javafx.scene.control.*;
    import javafx.scene.layout.StackPane;
    import javafx.stage.Stage;
    // pickOnBounds + -fx-shape bug.
    public class RoundButtonClick extends Application {
      public static void main(String[] args) { launch(args); }
      @Override public void start(Stage stage) throws Exception {
        ToggleButton button = new ToggleButton("Add");
        /* set the shape of button to be circle */
        button.setStyle(
                "-fx-font-size: 40; -fx-shape: \"m 307.14285,366.64789 c 27.7,0 50,22.3 50,50 0,27.7 -22.3,50 -50,50 -27.7,0 -50,-22.3 -50,-50 0,-27.7 22.3,-50 50,-50 z\";"
        );
        button.setMinHeight(Button.USE_PREF_SIZE);
        button.prefHeightProperty().bind(button.widthProperty());
        button.setMaxHeight(Button.USE_PREF_SIZE);
        button.setPickOnBounds(true);
    //    clip based workaround.
    //    Circle circularClip = new Circle();
    //    circularClip.centerXProperty().bind(button.widthProperty().divide(2));
    //    circularClip.centerYProperty().bind(button.heightProperty().divide(2));
    //    circularClip.radiusProperty().bind(button.widthProperty().divide(2));
    //    button.setClip(circularClip);
        StackPane layout = new StackPane();
        layout.getChildren().setAll(
                button
        );
        layout.setStyle("-fx-background-color: cornsilk; -fx-padding: 20px;");
      
        stage.setScene(new Scene(layout, 180, 180));
        stage.show();
      }
    }
    
    

     

    To reproduce, run the attached sample, click inside the circular ToggleButton and the ToggleButton will toggle as expected.

    Click outside the ToggleButton anywhere within the enclosing rectangular bounds of the button in it's parent and the ToggleButton will still toggle, even though it is not supposed to based on it's shape and the pickOnBounds setting.

     

    Test system was jdk8b101, win7 64bit.

     

    As a workaround, to make pickOnBounds work with a circular node defined via -fx-shape, you can set a clip on the node as shown in the attached sample code.

  • 7. Re: How to realize the functionality like the JLayer in Swing?
    963782 Newbie
    Currently Being Moderated

    thanks for your reply. Bug has been reported https://javafx-jira.kenai.com/browse/RT-32228

     

    For your workaround suggested, I have a question: if the SVG shape specifed is not circular but a a more complex or even irregular shape such as a T-shirt shape, the clip will be difficult to set, especially its position and size, as the T-shirt shape is designed by using Inkscape and -fx-shape can auto-center and auto-size it to fit the button.

  • 8. Re: How to realize the functionality like the JLayer in Swing?
    jsmith Guru
    Currently Being Moderated

    > if the SVG shape specifed is not circular but a a more complex or even irregular shape


    Then use an SVGPath for the clip rather than a Circle.


    > can auto-center and auto-size it to fit the button


    Use a StackPane containing an SVGPath for the clip.

  • 9. Re: How to realize the functionality like the JLayer in Swing?
    963782 Newbie
    Currently Being Moderated

    Thanks for your reply. I tried your suggestion:

    > can auto-center and auto-size it to fit the button


    Use a StackPane containing an SVGPath for the clip.


    But it does not work:

    Button button = new Button("OK");
      SVGPath svg = new SVGPath();
      svg.setContent("m 307.14285,366.64789 c 27.7,0 50,22.3 50,50 0,27.7 -22.3,50 -50,50 -27.7,0 -50,-22.3 -50,-50 0,-27.7 22.3,-50 50,-50 z");
      StackPane clip = new StackPane();
      clip.getChildren().add(svg);
      button.setClip(clip);

    I think the SVGPath is type of Shape and so not resizable, which makes it difficult.

  • 10. Re: How to realize the functionality like the JLayer in Swing?
    963782 Newbie
    Currently Being Moderated

    I tried also:

    final Button button = new Button("OK");
    SVGPath svg = new SVGPath();
    svg.setContent("m 307.14285,366.64789 c 27.7,0 50,22.3 50,50 0,27.7 -22.3,50 -50,50 -27.7,0 -50,-22.3 -50,-50 0,-27.7 22.3,-50 50,-50 z");
    final Bounds bounds = svg.getLayoutBounds();
    vg.scaleXProperty().bind(new DoubleBinding() {
      {
      super.bind(button.widthProperty());
      }
      @Override
      protected double computeValue() {
      return button.getWidth() / bounds.getWidth();
      }
    });
    svg.scaleYProperty().bind(new DoubleBinding() {
      {
      super.bind(button.heightProperty());
      }
      @Override
      protected double computeValue() {
      return button.getHeight() / bounds.getHeight();
      }
    });
    StackPane clip = new StackPane();
    clip.getChildren().add(svg);
    button.setClip(clip);

    But it still does not work.

  • 11. Re: How to realize the functionality like the JLayer in Swing?
    963782 Newbie
    Currently Being Moderated

    I need your help about the use of clip for Node. Thanks in advance

  • 12. Re: How to realize the functionality like the JLayer in Swing?
    jsmith Guru
    Currently Being Moderated

    As I mentioned earlier, "I suggest you log a bug regarding this."

     

    You can include my earlier sample code as an sscce in the bug filing.

     

    Please post back the bug reference in the thread. Thanks!

  • 13. Re: How to realize the functionality like the JLayer in Swing?
    jsmith Guru
    Currently Being Moderated

    Additionally, it seems to me that using an SVGPath as a clip also doesn't work (if I try to do that, rather than being appropriately clipped the underlying node is not visible), so that seems like a separate bug to file (perhaps I missed something, but I can't get it to work). 

     

    I have no workaround for the SVGPath clipping failure at the moment.

     

    You could try converting the SVGPath commands to a Path and seeing if that will work as a clip.

  • 14. Re: How to realize the functionality like the JLayer in Swing?
    963782 Newbie
    Currently Being Moderated

    SVGPath works, but you should specify the orign of the shape relative to the node. So use the layoutX or translateX property.

Legend

  • Correct Answers - 10 points
  • Helpful Answers - 5 points