Project

General

Profile

Bug #6167

Mouse event processing refactoring/improvement

Added by Vladimir Tsichevski over 2 years ago. Updated over 2 years ago.

Status:
New
Priority:
Normal
Assignee:
-
Target version:
-
Start date:
Due date:
% Done:

0%

billable:
No
vendor_id:
GCD
case_num:
version_reported:
version_resolved:

issue6013-final-14.03.2022.diff Magnifier (161 KB) Vladimir Tsichevski, 03/14/2022 10:59 AM

6013-mouse-click.p Magnifier (1.88 KB) Vladimir Tsichevski, 03/14/2022 11:35 AM

6013-mouse-up.p Magnifier (1.54 KB) Vladimir Tsichevski, 03/14/2022 11:35 AM


Related issues

Related to User Interface - Bug #6074: Movable widget problem Closed
Related to User Interface - Bug #6077: Movable widget problem, when using both mouse buttons New

History

#2 Updated by Vladimir Tsichevski over 2 years ago

This issue is created after #6013, all customer-specific material removed.

The main issue.

In 4gl, the MOUSE_RELEASED and MOUSE_CLICKED events are dispatched to (fire corresponding triggers for) widgets, which are under mouse pointer at the moment mouse button was released. In FWD this is not so: in most cases, these events are dispatched to the same widget as was under mouse pointer at the moment mouse button was pressed.

Test programs:

1. 6013-mouse-click.p. In this example program, two rectangle widgets are DEFINEd: outerRect and innerRect. The innerRect is located inside the outerRect and overlaps it almost entirely. The innerRect is initially hidden (innerRect:HIDDEN = TRUE). When outerRect receives a mouse down event, the innerRect becomes visible. When innerRect receives a mouse down event, the innerRect becomes hidden again. MOUSE-DOWN and MOUSE_CLICKED events are reported with corresponding MESSAGEs for both widgets.

The expected behavior is as follows:

When the user click the mouse:

  1. outerRect receives MOUSE_PRESSED
  2. innerRect receives the MOUSE_CLICKED event.

When the user click the mouse second time (above the innerRect):

  1. innerRect receives MOUSE_PRESSED
  2. outerRect receives the MOUSE_CLICKED event.

2. 6013-mouse-up.p. Same as 6013-mouse-click.p, but for MOUSE_RELEASED event instead of MOUSE_CLICKED.

I see the problem as follows:

The WEB and Swing clients use different approaches when choosing the widget to dispatch mouse click event to:

  1. In Swing the event target is selected internally by Swing in sun.awt.X11.XWindow:handleButtonPressRelease(XEvent xev). For mouse click Swing selects the same target as the widget the mouse press event was dispatched to.
  2. In WEB the mouse click target is first selected by JavaScript code at the browser side, then, at the Java client side, if this target is a container, calls findMouseSource(NativePoint) to select by default the child widget at the given point.

Usually these two approaches give the same result, except in our case when a new widget is created between mouse press and mouse release events.

Other attachments:

  1. issue6013-final-14.03.2022.diff the cumulative patch for all issues listed in #6013.

#3 Updated by Vladimir Tsichevski over 2 years ago

WEB event slippage problem

I noticed the following problem with WEB mouse events synchronization:

Situation 1. Slow user, fast client/network. Expected program behavior.
  1. The user presses mouse button above widget1, the MOUSE_PRESSED browser queues event for widget1.
  2. Java client receives the MOUSE_PRESSED event and creates widget2 over widget1 as the result, and sends updates to browser.
  3. Browser receives updates, and registers widget2 in its widget lists.
  4. After awhile, the user releases mouse button at the same point, the MOUSE_RELEASED browser queues event for the new widget2.
  5. widget2 receives the MOUSE_RELEASED event, as expected.
Situation 2. Fast user, slow client/network. Wrong program behavior.
  1. The user presses mouse button above widget1, the MOUSE_PRESSED browser queues event for widget1
  2. With minimal delay, the user releases mouse button at the same point.
  3. Browser queues the MOUSE_RELEASED event for widget1
  4. Java client receives the MOUSE_PRESSED event and creates widget2 over widget1 as the result, and sends updates to browser.
  5. Browser receives updates, and registers widget2 in its widget lists.
  6. widget1 receives the MOUSE_RELEASED event, which is not expected.

I noticed this problem manifestation after I set a few conditional BPs in the Java client code, so my client gets very slow.

Moving the mouse event source calculation to the Java side solves this problem.

#4 Updated by Vladimir Tsichevski over 2 years ago

  • Related to Bug #6074: Movable widget problem added

#5 Updated by Vladimir Tsichevski over 2 years ago

  • Related to Bug #6077: Movable widget problem, when using both mouse buttons added

#6 Updated by Vladimir Tsichevski over 2 years ago

Another problem: in WEB, when the mouse enters a window area, the MOUSE_ENTER event for the window is sent twice from the browser side.

#7 Updated by Vladimir Tsichevski over 2 years ago

Another issue: in WEB, while no mouse buttons is pressed, all mouse events generated have button=1 instead of expected button=0.

#8 Updated by Vladimir Tsichevski over 2 years ago

The patch description

Main ideas
  1. The mouse released event must be dispatched to the widget which is currently under mouse pointer at the moment the mouse button was released (this fixes the original #6013 issue).
  2. Both Swing and WEB drivers must produce exactly the same output events, before this change this was not so:
    1. The WEB driver operation deeply depends on the values returned by widget mouseActions method, the Swing driver ignored these.
    2. The Swing driver produces meaningful values of the button event field for all mouse-related event. Values set by WEB driver for events other than press/release/click are undefined. Meanwhile, these values are references in FWD outside the press/release/click mouse handlers.
    3. mouse direct operations must operate on widgets other than that used to dispatch events. For all drag-type operation the mouse release event should be dispatched to the same widget which reseived the correspondent mouse release event. Meanwhile, mouse direct operations implementations in Swing and WEB drivers are based on principally different principles: in WEB completely independent sets of system-wide event handlers are used for "normal" mouse event processing and mouse direct operations. In Swing both kinds of processing are based on mouse actions, which already attached to the same widget.
  3. Widgets must be capable to participate in defining the final mouse source when events are dispatched. The decision must be taken not only based on the mouse coordinates, but either on the event type.
Main changes
  1. The mouse released event is now dispatched to the widget which is currently under mouse pointer at the moment the mouse button was released both for WEB and Swing driver.
  2. Swing driver operation now depends on the values returned by widget mouseActions method, in the same manner as the WEB driver.
  3. The WEB driver produces meaningful values of the button event field for all mouse-related event using the algorithm similar to that the Swing (and hence Swing driver) uses.
  4. Both Swing and WEB driver now use the same findMouseSource(eventId, MouseEvt) when calculating the widget to dispatch mouse events to. The selection is based on:
    1. the event type (a new feature)
    2. the mouse pointer coordinates (an old feature)
    3. widget mouse event subscription (the values returned by widget mouseActions method)(a new feature)
  5. in Swing mouse direct operations now operate on widgets other than that used to dispatch events. This change is implemented in MouseHandler.handleMouseEvent(int sourceID, MouseEvent evt). The original Swind driver mouse actions-based architecture was not destroyed, since this would be a change too huge for one task.
  6. Widgets are now capable to participate in defining the final mouse source when events are dispatched.
Downsides
  1. In WEB, mouse source widgets computed at the browser side are ignored at the Java side in favor of the widgets computed at the Java side. So, part of JS code is currently is not used.
  2. The mouse actions-based code in Swing driver should be probably re-designed. IMO this architecture is not very suitable to the task.
Other changes

Multiple small fixes, see the descriptions below, which are also placed as the history entries in corresponding classes.

Changes by classes
src/com/goldencode/p2j/ui/chui/ThinClient.java
  1. Method processProgressEvent: results of calls to event.id(), and
    event.source() extracted to local variables (otherwise these calls are
    performed >40 times in the method code).
src/com/goldencode/p2j/ui/client/TopLevelWindow.java
  1. A new private field mousePressedSource is introduced. This field remembers the widget the
    mouse was pressed upon, and the value is used as the mouse event target during drag operation.
  2. findMouseSource(MouseEvt) implementation added. It uses mousePressedSource for drag
    operations, otherwise calls findMouseSource(evtID, mousePoint).
src/com/goldencode/p2j/ui/client/WindowManager.java
  1. getNextActiveWindow code refactored, so compilator complains of "Unlikely argument type" no
    more.
  1. processWindowEvent: # the mouse source stored with the event by the driver used instead of running
    window.findMouseSource(evt) second time. Note: after this change, the call to
    findMouseSource is executed only once by the driver (check this!) (previously
    findMouseSource was called multiple times during the event dispatching and processing, which
    is waste of CPU time, and probably may result in incorrect returned value)
    1. Local variable parent renamed to mouseSourceWithId, since the old name was misleading.
src/com/goldencode/p2j/ui/client/gui/ModalWindow.java
  1. findMouseSource overloaded: delegates request to windowPane.
src/com/goldencode/p2j/ui/client/gui/driver/MouseDirectManipulation.java
  1. processDrag(MouseEvent, boolean): unused second argument removed.
  2. Unused selectSingleWidget(false) removed
  3. selectSingleWidget(boolean): method argument inlined and removed (always true), assertion
    added to check this widget is selected at the moment of the call.
  4. Unused private method removed: deselectEverythingExcept(Widget)
  5. Methods deselectFrameWidgets(Frame) deselectFrameWidgetsExcept(Frame, Widget) refactored
    into one deselectFrameWidgets(Frame) (the second "except" parameter is not used anymore).
  6. Unused private method deselectEverythingExcept(Widget) removed.
  7. Private method deselectEverything() inlined (only single use).
  8. Unused private methods deselectNestedFrames(Frame), deselectNestedFramesExcept(Frame)
    removed.
  9. mousePressed refactored: if (!widgetResizing) replaced by more meaningful if
    (rowResizing)
    ; if/else replaced by early return; two conditional block with !isExtended(e)
    as part of condition replaced by one block.
src/com/goldencode/p2j/ui/client/gui/driver/MouseEmptySelector.java
  1. mousePressed() refactored to eliminate repeatable calls and unneeded Java casts and instanceof
    statements.
src/com/goldencode/p2j/ui/client/gui/driver/MouseHandler.java
  1. Method findMouseSource(MouseEvent) pulled up from SwingMouseHandler to make it accessible
    also from WebMouseHandler.
  2. Methods processWidgetActions, handleMouseEventIfMovable, handleMouseEventIfSelectable inlined
  3. Method applyMouseEvent(Object mouseAdapter, MouseEvent evt) now accepts all suitably types of mouse handlers as the first argument.
  4. Method canProcessMouse declared static
  5. Field mousePressedSourceID added to local context to track the last mouse pressed widget.
src/com/goldencode/p2j/ui/client/gui/driver/swing/SwingMouseHandler.java
  1. mouseEntered made no-op: all necessary actions are taken in the first mouse move event. The
    reason is that when a mouse pointer enters a widget, two events are generated simultaneously:
    the ENTER and the MOVE events.
  2. mouseExited: call to findMouseSource eliminated, the correct mouse source is in
    lastHoveredWidget field already.
  3. mouseDragged, mouseWheelMoved: the mouse source widget is explicitly passed to
    postMouseEvent.
  4. mouseMoved: # where is no need to execute code as a Runnable exit lambda, so the lambda body was inlined. # the complex method logic made simple: # if the widget under mouse was not changed, then do nothing in this method (just call super method) and exit. # otherwise if previous widget (lastHoveredWidget) is not null, then send EXIT event to it. # send ENTER event to the new widget.
  5. findMouseSource(MouseEvent): the local variable parent renamed to mouseSourceWithId,
    since the old name was misleading.
  6. processAction(MouseEvent): the value returned by findMouseSource(e) used instead of
    null.
  7. processAction: the argument is asserted never be null; method made static and renamed to
    processActions.
  8. Method findMouseSource(MouseEvent) pulled up to MouseHandler to make it accessible from
    WebMouseHandler.
src/com/goldencode/p2j/ui/client/gui/driver/web/WebMouseHandler.java
  1. raiseMouseEvent: # Method MouseHandler.findMouseSource(MouseEvent) is used instead of
    findMouseSource(w.findMouseSource(eventID)). # The unused widgetId arguments eliminated.
src/com/goldencode/p2j/ui/client/widget/Widget.java
  1. new findMouseSource(w.findMouseSource(eventID, NativePoint)) method introduced instead of
    obsolete findMouseSource(w.findMouseSource(eventID)) along with default implementation. This
    method returns 'this' iff widget is visible and enabled and mouse event type matches mouse
    actions defined in this widget. Otherwise @null
    is returned.
src/com/goldencode/p2j/ui/client/gui/driver/web/GuiWebDriver.java
  1. raiseMouseEvent: an additional widgetId parameter is passed to window raiseMouseEvent.
  2. Multiple cosmetic fixes: missing @Override annotations added, methods made static when possible, closable resource access wrapped with try() statement, unneeded Java casts removed.
src/com/goldencode/p2j/ui/client/widget/AbstractContainer.java
  1. findMouseSource(w.findMouseSource(eventID, NativePoint)) method is overriden.
src/com/goldencode/p2j/ui/client/gui/WindowGuiImpl.java
  1. findMouseSource(MouseEvent) moved to TopLevelWindow.
  2. init(): now that true mouse source is WindowTitle calculated instead of WindowTitleBar, WindowTitle is registered with gd.registerMoveableWidget instead of WindowTitleBar.
ButtonListGuiImpl
  1. Added overrides for findMouseSource(eventID, NativePoint) for 3 subclasses made after the corresponding findMouseSource(NativePoint) methods.
  2. processKeyEvent(KeyInput keyEvent): removed series of if/ifelse, which does nothing.
  3. ItemsControl.findMouseSource(MouseEvent): removed if (w == null) condition, which is never met.
src/com/goldencode/p2j/ui/client/gui/WindowTitleBar.java
  1. unused import statement(s) removed
  2. duplicate interfaces removed from the class definition
  3. The allButtons field made constant (final).
  4. The class fixed in static method call: TopLevelWindow.getWindowDefaultIcon() (was Window).
  5. Missing @Override annotations were added
  6. toString 8 lines of code refactored to 1.
  7. loadIcon method: access to a closable resource surrounded by try/catch block.
  8. Internal WindowTitle class access restriction weakened to allow access from outside of this class
  9. Overridden findMouseSource(int eventId, NativePoint p) introduced in the previous release was removed: special treatment is no more needed for this container widget children.
src/com/goldencode/p2j/ui/client/gui/ButtonListGuiImpl.java
  1. processKeyEvent: piece of code doing nothing removed.
  2. findMouseSource(NativePoint) fixed.
  3. findMouseSource(eventId, NativePoint) was overriden.
  4. setSelected: missing Javadocs added
src/com/goldencode/p2j/ui/client/gui/CaptionButton.java
  1. mouseActions: MouseEvent.MOUSE_MOVED was added to the list of actions.
src/com/goldencode/p2j/ui/client/gui/EditorGuiImpl.java
  1. findMouseSource(eventId, NativePoint) was overriden.
src/com/goldencode/p2j/ui/client/gui/driver/web/WebMouseDirectManipulation.java
  1. Javadocs and other cosmetic fixes.
src/com/goldencode/p2j/ui/client/gui/driver/web/res/p2j.mouse.js
  1. Event listeners removed for "mouseenter" and "mouseleave": they were duplicating "mouseout" and "mouseover" listeners in wrong order and resulted in duplicate events sent to Java.
  2. Function inlined: sendMouseExit, sendMouseEnter
  3. sendMouseEvent renamed to sendMouseOrOSEvent, Javadocs fixed
  4. processMouseMove simplified and fixed: See #6013-94 for the description.
src/com/goldencode/p2j/ui/client/gui/driver/web/res/p2j.screen.js
  1. The mouseButtonDownStack variable added to keep last mouse button pressed/released state stack.
  2. evt.preventSendToApplication is no more used to prevent some event handlers. evt.preventDefault() is used instead.
  3. event listeners are now not registered for "mouseenter", "mouseleave", "mouseover" and "mouselout" events.
  4. trivial sendExplicitMouseEvent function inlined.
  5. sendMouseEvent: a correct button field of output event is now calculated for all events (not only press/release/click events).
  6. getModifiersBitMask inlined
  7. checkMessageBlasterEvent: unused mop argument removed.

Also available in: Atom PDF