1

Just a quick question which is on my mind right now:

I have a JavaFX application that contains (among others) a ScrollPane and I need to capture the MouseClicked event of that ScrollPane. That itself is actually no problem, except that I only need to handle the event if the event target is an instance of a Rectangle, ToggleButton or a ScrollPaneSkin. That is actually quite easy, too, I know. Right now, I have the following code:

@FXML
void scrollPaneOnMouseClicked(MouseEvent event) {
    System.out.println(event.getTarget().getClass().getName());
    System.out.println(event.getTarget() instanceof  ScrollPaneSkin);
    if (event.getTarget() instanceof RoomRectangle || event.getTarget() instanceof ToggleButton || event.getTarget() instanceof ScrollPaneSkin) {
        // handle
    }
}

except that event.getTarget() instanceof ScrollPaneSkin says false even if System.out.println(event.getTarget().getClass().getName()); outputs com.sun.javafx.scene.control.skin.ScrollPaneSkin$4 (and the debugger confirms that).

I also tried event.getTarget() instanceof ScrollPaneSkin$4 which resulted in a "cannot find symbol"-error.

What did I miss here?

vatbub
  • 2,713
  • 18
  • 41

2 Answers2

3

Ok, I fixed it by myself. The problem was quite simple and the reason is $4. As I just found out, the $4 points to an anonymous inner class of ScrollPaneSkin that obviously cannot be accessed from outside. That means that the target really was not an instance of ScrollPaneSkin but rather an instance of that inner class. The only workaround is to use event.getTarget.getClass().getName() and do a String-comparison. (Solution taken from here)

Community
  • 1
  • 1
vatbub
  • 2,713
  • 18
  • 41
2

Update

ScrollPaneSkin is now part of the public JavaFX API in more recent JavaFX versions, however, the rest of this answer regarding naming for the anonymous inner class still applies.


ScrollPaneSkin$4 is likely an instance of name mangling for anonymous inner class created inside the ScrollPaneSkin. This is probably why your code event.getTarget() instanceof ScrollPaneSkin does not work (because the target is actually the instance of the anonymous inner class rather than a skin itself).

The scope of anonymous classes is confined to their parent class, so the compiler must produce a "qualified" public name for the inner class, to avoid conflict where other classes with the same name (inner or not) exist in the same namespace. Similarly, anonymous classes must have "fake" public names generated for them (as the concept of anonymous classes only exists in the compiler, not the runtime). So, compiling the following java program

public class foo {
    class bar {
        public int x;
    }

    public void zark () {
        Object f = new Object () {
            public String toString() {
                return "hello";
            }
        };
    }
}

will produce three .class files:

  • foo.class, containing the main (outer) class foo
  • foo$bar.class, containing the named inner class foo.bar
  • foo$1.class, containing the anonymous inner class (local to method foo.zark)

As ScrollPaneSkin is a com.sun which is not supported public API, writing code based upon it is probably not a good idea as it may be fragile and prone to error due to the internal implementation of ScrollPaneSkin, which could change from Java to version to version. I don't have a further suggestion on how you to accomplish what you wish to accomplish.

jewelsea
  • 150,031
  • 14
  • 366
  • 406
  • Yeah I know that it actually is not a good idea to code upon skins but I don't have much of a choice. I have a particular use case where the target turns out to be that `ScrollPaneSkin$4`-thing and the event target is really the only thing that I can use to distinguish when to handle the event and when not (I looked at other things too). I finally ended up doing a string comparison of the class name (see my own answer below). Thanks anyway :) – vatbub Jan 04 '17 at 00:17