Uploaded image for project: 'JDK'
  1. JDK
  2. JDK-8194704

Text/TextFlow hitTest() javadoc

XMLWordPrintable

    • b21
    • generic
    • generic

      FULL PRODUCT VERSION :
      java version "10-ea" 2018-03-20
      Java(TM) SE Runtime Environment 18.3 (build 10-ea+37)
      Java HotSpot(TM) 64-Bit Server VM 18.3 (build 10-ea+37, mixed mode)

      ADDITIONAL OS VERSION INFORMATION :
      Microsoft Windows [Version 10.0.16299.125]

      A DESCRIPTION OF THE PROBLEM :
      The description of the TextFlow HitTest is:
      "Maps local point to index in the content."
      This does not return the node index at the given coordinates. Instead it returns the character index within the Text node.

      STEPS TO FOLLOW TO REPRODUCE THE PROBLEM :
      Create TextFlow contining several nodes. Add mouse listener to each node. Use mouse coordinates as input to TextFlow HitTest. Print the HitInfo that is returned.
      Also print HitInfo for the Text node HitTest. The two HitInfo are identical. Compare actual node index to TextFlow HitInfo results; they don't match.

      EXPECTED VERSUS ACTUAL BEHAVIOR :
      EXPECTED -
      The TextFlow HitTest should return the node index at the input coordinates.
      ACTUAL -
      The TextFlow HitTest returned the same results as the Text node HitTest.

      REPRODUCIBILITY :
      This bug can be reproduced always.

      ---------- BEGIN SOURCE ----------
      //
      package tools;
      //
      import javafx.application.Application;
      import javafx.scene.Scene;
      import javafx.stage.Stage;
      import javafx.scene.Node;
      import java.util.logging.Level;
      import java.util.logging.Logger;
      import javafx.geometry.Bounds;
      import javafx.geometry.Insets;
      import javafx.geometry.Pos;
      import javafx.geometry.Point2D;
      import javafx.scene.shape.Rectangle;
      import javafx.scene.text.Font;
      import javafx.scene.text.FontWeight;
      import javafx.scene.text.Text;
      import javafx.scene.text.HitInfo;
      import javafx.scene.text.TextFlow;
      import javafx.scene.layout.VBox;
      import javafx.scene.layout.BorderPane;
      import javafx.scene.layout.StackPane;
      import javafx.scene.input.MouseEvent;
      import javafx.scene.input.MouseButton;
      import javafx.scene.paint.Color;
      import javafx.collections.ObservableMap;
      import javafx.event.EventHandler;
      //
      //
      public class FlowHit extends Application {
      /*
      This app tests the HitTest method for Text and TextFlow. Both methods
      return the same HitInfo.

      Is this a bug in TextFlow?
      */

      private static final Logger LOG = Logger.getLogger(FlowHit.class.getName());
      //
      //init fonts
      private Font mainFont = Font.font("DejaVu Serif", FontWeight.NORMAL, 20);
      //
      //init panes
      private BorderPane bp = new BorderPane();
      private StackPane sp = new StackPane();
      private VBox vbox = new VBox();
      private TextFlow flow = new TextFlow();
      //
      //init sample string of words
      private String s1 = "Electromagnetic waves cover a wide spectrum from radio waves to gamma rays. Waves can interfere. "
          + "Constructive interference occurs when two waves are in phase. ";

          /**
           * Default Constructor
           */
          public FlowHit() {
          }

      public void test(){
      try{
      init();

      addText(flow, s1);

      vbox.getChildren().add(flow);


      } catch (Exception ex) {
      LOG.log(Level.SEVERE, ex.getMessage(), ex);
      throw new RuntimeException(ex);
      }
      }

      public void init(){
      try{
      //insets: top, right, bottom, left
      bp.setPadding(new Insets(5, 5, 5, 5));
      bp.setStyle(" -fx-border-color: green;-fx-border-width: 2px;-fx-border-style: solid;");
      bp.setPrefSize(500,400);
      //
      sp.setPadding(new Insets(10, 5, 10, 5));
      sp.setStyle(" -fx-border-color: blue;-fx-border-width: 1px;-fx-border-style: solid;");
      //
      vbox.setSpacing(8);
      vbox.setPadding(new Insets(15, 5, 15, 5));
      vbox.setStyle(" -fx-border-color: red;-fx-border-width: 1px;-fx-border-style: solid;");
      //
      } catch (Exception ex) {
      LOG.log(Level.SEVERE, ex.getMessage(), ex);
      throw new RuntimeException(ex);
      }
      }


      public void addText(TextFlow flow, String s){
      try{
      LOG.log(Level.INFO, "\n\nText s: " + s);
      //split string into words
      String[] ss = s.split(" ");
      int len = ss.length;
      for(int i = 0; i<len; i++){
      //create a word
      Text tx = new Text(ss[i] + " ");
      //set font on each word
      tx.setFont(mainFont);
      //add mouse click handler
      tx.setOnMouseReleased(new EventHandler<MouseEvent>() {
      @Override
      public void handle(MouseEvent event) {
      if (event.getButton() == MouseButton.PRIMARY) {
      event.consume();
      double mx = event.getX();
      double my = event.getY();
      int index = flow.getChildren().indexOf(tx);
      //check baseline offset
      checkHit(flow, index, mx, my);
      }
      }
      });

      //add one word to flow
      flow.getChildren().add(tx);
      }
      flow.setStyle(" -fx-border-color: orange;-fx-border-width: 1px;-fx-border-style: solid;");

      } catch (Exception ex) {
      LOG.log(Level.SEVERE, ex.getMessage(), ex);
      throw new RuntimeException(ex);
      }
      }

      public void checkHit(TextFlow flow, int index, double mx, double my){
      //check hit info of selected node
      try{
      //
      Node n = flow.getChildren().get(index);
      Text t = (Text)n;
      //create rectangle around selected node (may be commented out)
      addBox(n);
      //find mouse point
      Point2D pt = new Point2D(mx, my);
      //
      //get Text HitInfo
      HitInfo hitText = t.hitTest(pt);
      //
      //get TextFlow HitInfo
      HitInfo hitFlow = flow.hitTest(pt);


      LOG.log(Level.INFO, "\n index: " + index + ", text: " + t.getText() + ", pt:" + pt + ", hitText:" + hitText + ", hitFlow:" + hitFlow );
      //
      } catch (Exception ex) {
      LOG.log(Level.SEVERE, ex.getMessage(), ex);
      throw new RuntimeException(ex);
      }
      }

      public void addBox(Node n){
      //add box around of selected node
      try{
      //
      //get text bounds within flow (parent)
      Bounds parentB = n.getBoundsInParent();
      // LOG.log(Level.INFO, "\n\n node bounds in parent: " + parentB);

      //convert to scene coords
      Bounds sceneB = flow.localToScene(parentB);
      // LOG.log(Level.INFO, "\n\n scene bounds: " + sceneB);

      //create rectangle in scene coords
      Rectangle r = new Rectangle( sceneB.getMinX(), sceneB.getMinY(), sceneB.getWidth(), sceneB.getHeight() );
      r.setStroke(Color.GREEN);
      r.setFill(Color.TRANSPARENT);
      //add rectangle to border pane
      bp.getChildren().add(r);
      //
      } catch (Exception ex) {
      LOG.log(Level.SEVERE, ex.getMessage(), ex);
      throw new RuntimeException(ex);
      }
      }


          @Override
          public void start(Stage primaryStage) throws Exception{

      //add textflow to vbox
      test();
      // add stack pane to outer pane
      sp.setAlignment(Pos.CENTER);
      sp.getChildren().add(vbox);
      bp.setCenter(sp);
      //
              // setup scene and stage
              Scene scene = new Scene( bp, 600, 500 );

              primaryStage.setScene(scene);
              primaryStage.sizeToScene();
              primaryStage.show();
      //check baselines of selected nodes
          }



          public static void main(String[] args) {
              launch(args);
          }
      }
      ---------- END SOURCE ----------

      CUSTOMER SUBMITTED WORKAROUND :
      Added mouse listener to every node within each TextFlow, and use ObservableList<Node> getChildren() -> indexOf(Object o) to determine the node index at the mouse location. Don't use the TextFlow HitTest.

            angorya Andy Goryachev
            webbuggrp Webbug Group
            Votes:
            0 Vote for this issue
            Watchers:
            7 Start watching this issue

              Created:
              Updated:
              Resolved: