Skip to content

Some MouseEvent behaviors cannot be safely overridden because hooks for them do not yet exist #357

@MichelMunoz

Description

@MichelMunoz

First, thanks for this very promising component.

Now, the observed behavior. In my code (a console widget) I wanted to reposition the cursor after a text selection was made (on mouse released, using moveTo), it worked well except when the drag ended out of the component area. Annoyed by the thing, I digged, and ended finding that it was somehow linked to hit computation and drag code ; when the drag (of selection) ends within the component all is fine, but when the drag ends outside the component the hit methods is called infinitelly and the moveTo doesnt work as expected.

Version used : the last one (7-M2), but the behavior was observed pre-7.

To reproduce/observe the problem, a "crude" approach :

  • create a subclass of StyleClassedTextArea , let's call it StyleClassedTextAreaExt, it only overrides the hit method to trace the calls and stack
@Override
    public CharacterHit hit(double x, double y) {
        CharacterHit hit = super.hit(x, y);
        System.out.println("HIT CALLED : ");
        (new Exception()).printStackTrace();
        return hit;
    }
  • create a basic fx app, border pane, and in center the StyleClassedTextAreaExt
  • launch the app, not in full screen, and keep an eye on console output
  • type some text in the text area
  • start a text selection in the text, keep the mouse pressed, move the cursor out of the text area to the left (same to the right), and the release the mouse
  • you then observe infinite calls in the logs

The observed stacks of the continuous calls are :

at fr.mmu.st2.console.ui.widget.console.StyleClassedTextAreaExt.hit(Console.java:246)
at org.fxmisc.richtext.StyledTextAreaBehavior.dragTo(StyledTextAreaBehavior.java:471)
at org.reactfx.value.Val.ifPresent(Val.java:151)
at org.fxmisc.richtext.StyledTextAreaBehavior.lambda$new$38(StyledTextAreaBehavior.java:259)
at org.reactfx.util.NonAccumulativeStreamNotifications.lambda$head$0(NotificationAccumulator.java:134)
at org.reactfx.ObservableBase.notifyObservers(ObservableBase.java:68)
at org.reactfx.ObservableBase.notifyObservers(ObservableBase.java:57)
at org.reactfx.ProperEventStream.emit(ProperEventStream.java:18)
at org.reactfx.util.NonAccumulativeStreamNotifications.lambda$head$0(NotificationAccumulator.java:134)
at org.reactfx.ObservableBase.notifyObservers(ObservableBase.java:68)
at org.reactfx.ObservableBase.notifyObservers(ObservableBase.java:57)
at org.reactfx.ProperEventStream.emit(ProperEventStream.java:18)
at org.reactfx.MappedStream.lambda$observeInputs$0(MappedStream.java:25)
at org.reactfx.util.NonAccumulativeStreamNotifications.lambda$head$0(NotificationAccumulator.java:134)
at org.reactfx.ObservableBase.notifyObservers(ObservableBase.java:68)
at org.reactfx.ObservableBase.notifyObservers(ObservableBase.java:57)
at org.reactfx.ProperEventStream.emit(ProperEventStream.java:18)
at org.reactfx.EmitBothOnEachStream.lambda$observeInputs$1(EmitBothOnEachStream.java:31)
at org.reactfx.util.NonAccumulativeStreamNotifications.lambda$head$0(NotificationAccumulator.java:134)
at org.reactfx.ObservableBase.notifyObservers(ObservableBase.java:68)
at org.reactfx.ObservableBase.notifyObservers(ObservableBase.java:57)
at org.reactfx.ProperEventStream.emit(ProperEventStream.java:18)
at org.reactfx.MappedStream.lambda$observeInputs$0(MappedStream.java:25)
at org.reactfx.util.NonAccumulativeStreamNotifications.lambda$head$0(NotificationAccumulator.java:134)
at org.reactfx.ObservableBase.notifyObservers(ObservableBase.java:68)
at org.reactfx.ObservableBase.notifyObservers(ObservableBase.java:57)
at org.reactfx.ProperEventStream.emit(ProperEventStream.java:18)
at org.reactfx.AccumulatingStream.lambda$observeInputs$0(AccumulatingStream.java:34)
at org.reactfx.util.NonAccumulativeStreamNotifications.lambda$head$0(NotificationAccumulator.java:134)
at org.reactfx.ObservableBase.notifyObservers(ObservableBase.java:68)
at org.reactfx.ObservableBase.notifyObservers(ObservableBase.java:57)
at org.reactfx.ProperEventStream.emit(ProperEventStream.java:18)
at org.reactfx.EventStreams$20$1.handle(EventStreams.java:421)
at javafx.animation.AnimationTimer$AnimationTimerReceiver.lambda$handle$484(AnimationTimer.java:57)
at java.security.AccessController.doPrivileged(Native Method)
at javafx.animation.AnimationTimer$AnimationTimerReceiver.handle(AnimationTimer.java:56)
at com.sun.scenario.animation.AbstractMasterTimer.timePulseImpl(AbstractMasterTimer.java:357)
at com.sun.scenario.animation.AbstractMasterTimer$MainLoop.run(AbstractMasterTimer.java:267)
at com.sun.javafx.tk.quantum.QuantumToolkit.pulse(QuantumToolkit.java:506)
at com.sun.javafx.tk.quantum.QuantumToolkit.pulse(QuantumToolkit.java:490)
at com.sun.javafx.tk.quantum.QuantumToolkit.lambda$runToolkit$404(QuantumToolkit.java:319)
at com.sun.glass.ui.InvokeLaterDispatcher$Future.run(InvokeLaterDispatcher.java:95)
at com.sun.glass.ui.win.WinApplication._runLoop(Native Method)
at com.sun.glass.ui.win.WinApplication.lambda$null$148(WinApplication.java:191)
at java.lang.Thread.run(Thread.java:745)

P.S.
If it is of any value, one point I observed in the code of your component while digging the problem :

  • in debug, while reproducing the problem, one can see that if you put breakpoints in StyledTextArea.hit(x,y) in the two first conditions (that should be triggered, I guess, once the mouse goes out of the component area) ; they are never triggered during the problem :
public CharacterHit hit(double x, double y) {
        VirtualFlowHit<Cell<Paragraph<PS, S>, ParagraphBox<PS, S>>> hit = virtualFlow.hit(x, y);
        if(hit.isBeforeCells()) {
            return CharacterHit.insertionAt(0);     <----- (breakpoint here) never true when mouse goes out
        } else if(hit.isAfterCells()) {
            return CharacterHit.insertionAt(getLength());      <----- (breakpoint here) never true when mouse goes out
        } else {
            int parIdx = hit.getCellIndex();
            int parOffset = getParagraphOffset(parIdx);
            ParagraphBox<PS, S> cell = hit.getCell().getNode();
            Point2D cellOffset = hit.getCellOffset();
            CharacterHit parHit = cell.hit(cellOffset);
            return parHit.offset(parOffset);
        }
    }
  • what is triggerred though (and should not in this case, I guess) is in StyledTextAreaBehavior, the start auto scroll :
private void mouseDragged(MouseEvent e) {
        // don't respond if disabled
        if(view.isDisabled()) {
            return;
        }

        // only respond to primary button alone
        if(e.getButton() != MouseButton.PRIMARY || e.isMiddleButtonDown() || e.isSecondaryButtonDown()) {
            return;
        }

        Point2D p = new Point2D(e.getX(), e.getY());
        if(view.getLayoutBounds().contains(p)) {
            dragTo(p);
            autoscrollTo.setValue(null); // stops auto-scroll
        } else {
            autoscrollTo.setValue(p);    // starts auto-scroll     <------ HERE : triggered twice before infinite loop starts
        }

        e.consume();
    }

Metadata

Metadata

Assignees

No one assigned

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions