Bug #6167
Mouse event processing refactoring/improvement
0%
Related issues
History
#2 Updated by Vladimir Tsichevski over 4 years ago
- File 6013-mouse-up.p
added - File issue6013-final-14.03.2022.diff
added - File 6013-mouse-click.p
added
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:
outerRectreceivesMOUSE_PRESSEDinnerRectreceives theMOUSE_CLICKEDevent.
When the user click the mouse second time (above the innerRect):
innerRectreceivesMOUSE_PRESSEDouterRectreceives theMOUSE_CLICKEDevent.
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:
- 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. - 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:
issue6013-final-14.03.2022.diffthe cumulative patch for all issues listed in #6013.
#3 Updated by Vladimir Tsichevski over 4 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.¶
- The user presses mouse button above
widget1, theMOUSE_PRESSEDbrowser queues event forwidget1. - Java client receives the
MOUSE_PRESSEDevent and createswidget2overwidget1as the result, and sends updates to browser. - Browser receives updates, and registers
widget2in its widget lists. - After awhile, the user releases mouse button at the same point, the
MOUSE_RELEASEDbrowser queues event for the newwidget2. widget2receives theMOUSE_RELEASEDevent, as expected.
Situation 2. Fast user, slow client/network. Wrong program behavior.¶
- The user presses mouse button above
widget1, theMOUSE_PRESSEDbrowser queues event forwidget1 - With minimal delay, the user releases mouse button at the same point.
- Browser queues the
MOUSE_RELEASEDevent forwidget1 - Java client receives the
MOUSE_PRESSEDevent and createswidget2overwidget1as the result, and sends updates to browser. - Browser receives updates, and registers
widget2in its widget lists. widget1receives theMOUSE_RELEASEDevent, 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 4 years ago
- Related to Bug #6074: Movable widget problem added
#5 Updated by Vladimir Tsichevski over 4 years ago
- Related to Bug #6077: Movable widget problem, when using both mouse buttons added
#6 Updated by Vladimir Tsichevski over 4 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 4 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 4 years ago
The patch description¶
Main ideas¶
- 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).
- Both Swing and WEB drivers must produce exactly the same output events, before this change this was not so:
- The WEB driver operation deeply depends on the values returned by widget
mouseActionsmethod, the Swing driver ignored these. - The Swing driver produces meaningful values of the
buttonevent 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. - 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.
- The WEB driver operation deeply depends on the values returned by widget
- 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¶
- 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.
- Swing driver operation now depends on the values returned by widget
mouseActionsmethod, in the same manner as the WEB driver. - The WEB driver produces meaningful values of the
buttonevent field for all mouse-related event using the algorithm similar to that the Swing (and hence Swing driver) uses. - 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:- the event type (a new feature)
- the mouse pointer coordinates (an old feature)
- widget mouse event subscription (the values returned by widget
mouseActionsmethod)(a new feature)
- 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. - Widgets are now capable to participate in defining the final mouse source when events are dispatched.
Downsides¶
- 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.
- 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¶
- 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¶
- A new private field
mousePressedSourceis introduced. This field remembers the widget the
mouse was pressed upon, and the value is used as the mouse event target during drag operation. findMouseSource(MouseEvt)implementation added. It usesmousePressedSourcefor drag
operations, otherwise callsfindMouseSource(evtID, mousePoint).
src/com/goldencode/p2j/ui/client/WindowManager.java¶
getNextActiveWindowcode refactored, so compilator complains of "Unlikely argument type" no
more.
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
findMouseSourceis executed only once by the driver (check this!) (previously
findMouseSourcewas called multiple times during the event dispatching and processing, which
is waste of CPU time, and probably may result in incorrect returned value)- Local variable
parentrenamed tomouseSourceWithId, since the old name was misleading.
- Local variable
src/com/goldencode/p2j/ui/client/gui/ModalWindow.java¶
findMouseSourceoverloaded: delegates request towindowPane.
src/com/goldencode/p2j/ui/client/gui/driver/MouseDirectManipulation.java¶
processDrag(MouseEvent, boolean): unused second argument removed.- Unused
selectSingleWidget(false)removed - selectSingleWidget(boolean): method argument inlined and removed (always
true), assertion
added to check this widget is selected at the moment of the call. - Unused private method removed:
deselectEverythingExcept(Widget) - Methods
deselectFrameWidgets(Frame)deselectFrameWidgetsExcept(Frame, Widget)refactored
into onedeselectFrameWidgets(Frame)(the second "except" parameter is not used anymore). - Unused private method
deselectEverythingExcept(Widget)removed. - Private method
deselectEverything()inlined (only single use). - Unused private methods
deselectNestedFrames(Frame),deselectNestedFramesExcept(Frame)
removed. mousePressedrefactored:if (!widgetResizing)replaced by more meaningfulif; if/else replaced by early return; two conditional block with
(rowResizing)!isExtended(e)
as part of condition replaced by one block.
src/com/goldencode/p2j/ui/client/gui/driver/MouseEmptySelector.java¶
- mousePressed() refactored to eliminate repeatable calls and unneeded Java casts and instanceof
statements.
src/com/goldencode/p2j/ui/client/gui/driver/MouseHandler.java¶
- Method
findMouseSource(MouseEvent)pulled up fromSwingMouseHandlerto make it accessible
also fromWebMouseHandler. - Methods
processWidgetActions,handleMouseEventIfMovable,handleMouseEventIfSelectableinlined - Method
applyMouseEvent(Object mouseAdapter, MouseEvent evt)now accepts all suitably types of mouse handlers as the first argument. - Method
canProcessMousedeclared static - Field
mousePressedSourceIDadded to local context to track the last mouse pressed widget.
src/com/goldencode/p2j/ui/client/gui/driver/swing/SwingMouseHandler.java¶
mouseEnteredmade 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:
theENTERand theMOVEevents.mouseExited: call tofindMouseSourceeliminated, the correct mouse source is in
lastHoveredWidgetfield already.mouseDragged,mouseWheelMoved: the mouse source widget is explicitly passed to
postMouseEvent.mouseMoved: # where is no need to execute code as aRunnable exitlambda, 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 sendEXITevent to it. # sendENTERevent to the new widget.findMouseSource(MouseEvent): the local variableparentrenamed tomouseSourceWithId,
since the old name was misleading.processAction(MouseEvent): the value returned byfindMouseSource(e)used instead of
null.processAction: the argument is asserted never benull; method made static and renamed to
processActions.- Method
findMouseSource(MouseEvent)pulled up to MouseHandler to make it accessible from
WebMouseHandler.
src/com/goldencode/p2j/ui/client/gui/driver/web/WebMouseHandler.java¶
raiseMouseEvent: # MethodMouseHandler.findMouseSource(MouseEvent)is used instead of
findMouseSource(w.findMouseSource(eventID)). # The unusedwidgetIdarguments eliminated.
src/com/goldencode/p2j/ui/client/widget/Widget.java¶
- new
findMouseSource(w.findMouseSource(eventID, NativePoint))method introduced instead of
obsoletefindMouseSource(w.findMouseSource(eventID)) along with default implementation. Thisis returned.
method returns 'this' iff widget is visible and enabled and mouse event type matches mouse
actions defined in this widget. Otherwise @null
src/com/goldencode/p2j/ui/client/gui/driver/web/GuiWebDriver.java¶
raiseMouseEvent: an additionalwidgetIdparameter is passed to windowraiseMouseEvent.- 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¶
findMouseSource(w.findMouseSource(eventID, NativePoint))method is overriden.
src/com/goldencode/p2j/ui/client/gui/WindowGuiImpl.java¶
findMouseSource(MouseEvent)moved toTopLevelWindow.init(): now that true mouse source isWindowTitlecalculated instead ofWindowTitleBar,WindowTitleis registered withgd.registerMoveableWidgetinstead ofWindowTitleBar.
ButtonListGuiImpl¶
- Added overrides for
findMouseSource(eventID, NativePoint)for 3 subclasses made after the correspondingfindMouseSource(NativePoint)methods. processKeyEvent(KeyInput keyEvent): removed series of if/ifelse, which does nothing.ItemsControl.findMouseSource(MouseEvent): removedif (w == null)condition, which is never met.
src/com/goldencode/p2j/ui/client/gui/WindowTitleBar.java¶
- unused import statement(s) removed
- duplicate interfaces removed from the class definition
- The
allButtonsfield made constant (final). - The class fixed in static method call: TopLevelWindow.getWindowDefaultIcon() (was Window).
- Missing @Override annotations were added
toString8 lines of code refactored to 1.loadIconmethod: access to a closable resource surrounded bytry/catchblock.- Internal
WindowTitleclass access restriction weakened to allow access from outside of this class - 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¶
processKeyEvent: piece of code doing nothing removed.findMouseSource(NativePoint)fixed.findMouseSource(eventId, NativePoint)was overriden.setSelected: missing Javadocs added
src/com/goldencode/p2j/ui/client/gui/CaptionButton.java¶
mouseActions:MouseEvent.MOUSE_MOVEDwas added to the list of actions.
src/com/goldencode/p2j/ui/client/gui/EditorGuiImpl.java¶
- findMouseSource(eventId, NativePoint) was overriden.
src/com/goldencode/p2j/ui/client/gui/driver/web/WebMouseDirectManipulation.java¶
- Javadocs and other cosmetic fixes.
src/com/goldencode/p2j/ui/client/gui/driver/web/res/p2j.mouse.js¶
- 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.
- Function inlined:
sendMouseExit,sendMouseEnter sendMouseEventrenamed tosendMouseOrOSEvent, Javadocs fixedprocessMouseMovesimplified and fixed: See #6013-94 for the description.
src/com/goldencode/p2j/ui/client/gui/driver/web/res/p2j.screen.js¶
- The
mouseButtonDownStackvariable added to keep last mouse button pressed/released state stack. evt.preventSendToApplicationis no more used to prevent some event handlers. evt.preventDefault() is used instead.- event listeners are now not registered for "mouseenter", "mouseleave", "mouseover" and "mouselout" events.
- trivial sendExplicitMouseEvent function inlined.
sendMouseEvent: a correctbuttonfield of output event is now calculated for all events (not only press/release/click events).getModifiersBitMaskinlinedcheckMessageBlasterEvent: unusedmopargument removed.