08 Data From Tag
Step 9/11 — 09 Hit-Testing
01 Rendering the Label Text 02 Using Text Utilities 03 Adding a Background Shape 04 Preferred Label Size 05 Render Performance 06 Text Alignment 07 Line Wrapping 08 Data From Tag 09 Hit-Testing 10 Visibility 11 Bounds
10 Visibility

# Customizing hit-testing

Styles not only provide a visual representation for graph items. They also offer crucial parts for interacting with said items. Hit-testing determines whether a particular item is at a given point in the canvas. It happens all the time during interaction, e.g. when selecting items, moving nodes, or creating edges.

The default implementation in the `LabelStyleBase` class only considers the label layout when hit-testing. When labels have shapes that deviate from the rectangular label layout, hit-testing has to be adjusted by providing a different implementation for the `isHit` method.

In this example, we have to include the "tail" of the speech balloon in the hit-test. We also take the hit-test radius into account. This value enables a bit of fuzziness when hit-testing the item, so you can still hit it when you’re slightly outside.

Note | For this demo, we increased the size of the speech balloon "tail".

    protected isHit(
      context: IInputModeContext,
      location: Point,
      label: ILabel
    ): boolean {
      const labelLayout = label.layout
      // first check if the label layout is hit
      if (labelLayout.hits(location, context.hitTestRadius)) {
        return true
      }
      // if the layout is not hit, we have to check the tail triangle.
      // instead of checking the real label layout, we pretend the tail triangle is placed
      // non-rotated at 0/0 and transform the hit-test location accordingly.

      // create the inverted layout transform
      const layoutTransform = LabelStyleBase.createLayoutTransform(
        context.canvasComponent!.createRenderContext(),
        labelLayout,
        true
      )
      layoutTransform.invert()
      // transform the location and subtract the tail position
      const transformedLocation = layoutTransform
        .transform(location)
        .subtract(new Point(labelLayout.width * 0.5, labelLayout.height))

      // check the rectangular tail bounds
      const tailBounds = new Rect(0, 0, tailWidth, tailHeight)
      if (
        !tailBounds.containsWithEps(transformedLocation, context.hitTestRadius)
      ) {
        return false
      }

      // the location is inside the tail bounds - check if it's inside the triangle
      const tailHeightAtLocationX =
        tailHeight * ((tailWidth - transformedLocation.x) / tailWidth)
      return (
        transformedLocation.y <= tailHeightAtLocationX + context.hitTestRadius
      )
    }

Note | Try this implementation by hovering both labels. Notice that the red labels, which uses the default hit-test, does not detect hits on the tail. The yellow labels use the adjusted hit-test code.

10 Visibility