Bug #11343
Alert-box suddenly disppears when a specific item is selected from a drop-down list using UP/DOWN keys
100%
History
#1 Updated by Delia Mitric 4 months ago
This is the testcase:
DEFINE VARIABLE roomNumber AS CHARACTER
LABEL "Room Number"
VIEW-AS COMBO-BOX INNER-LINES 5
LIST-ITEMS "101","102","203","204"
SIZE 16 BY 1 NO-UNDO.
DEFINE FRAME fMain roomNumber.
ON VALUE-CHANGED OF roomNumber DO:
IF SELF:SCREEN-VALUE = "203" THEN DO:
MESSAGE "You can't chose room 203!"
VIEW-AS ALERT-BOX INFORMATION BUTTONS OK TITLE "Room Selected".
END.
END.
CURRENT-WINDOW:WIDTH-CHARS = 100.
ENABLE ALL WITH FRAME fMain.
WAIT-FOR CLOSE OF CURRENT-WINDOW.
Found the issue in a Customer GUI app, similar scenario.
#5 Updated by Delia Mitric 4 months ago
The issue seems to come from the fact that the OverlayWindow (the DropDown window) isn't close at the moment when the AlertBox Window is shown (it is about an InvocationEvent ran when both windows are displayed/visible)...
We should close the DropDown while iterating through the list using UP/DOWN keys in a similar way the DropDown is hidden when we click on an list item.
#8 Updated by Delia Mitric 4 months ago
I've committed a possible partial solution to 11343a branch rev. 16503 : the OverlayWindow is hidden before the AlertBox is created to avoid destroying the AlertBox suddenly (without interacting with it).
The next step is to fix the "frozen app" after we close the AlerBox window. This seems to happen because of the comboBox dropBox widget/because of a MOUSE-PRESSED event on the COMBO-BOX.
I'm looking into it..
#11 Updated by Delia Mitric 4 months ago
- % Done changed from 10 to 100
- Status changed from WIP to Review
Committed the complete solution to 11343a rev. 16504
The changes are necessary to:- prevent the alert-box to be suddenly closed
- avoid the frozen app after the alert-box is closed
- prevent the generation of a second
VALUE-CHANGEDevent for theCOMBO-BOX
Hynek, please review.
Thanks!
#13 Updated by Hynek Cihlar 4 months ago
- Status changed from Review to WIP
- % Done changed from 100 to 90
Code review 11343a.
- [MAJOR] design
ComboBox/ThinClientalert-box handling: The patch treats the symptom, not the cause. The root bug is that the keyboard selection path firesVALUE-CHANGEDwhile the drop-downOverlayWindowis still on screen, whereas the mouse-click path closes the overlay first and then firesVALUE-CHANGED. The proper fix is to align the keyboard commit path with the mouse path (close the drop-down before firing the trigger). Doing so eliminates every secondary problem the current patch has to work around: no overlay-over-alert-box, no re-entrancy into the exit path from inside a running trigger (so no need for a visual-only half-hide), no duplicateVALUE-CHANGEDon the post-modal close (so no need fordeferredProgressTrigger = false), and no staleactiveComboBox. The two newComboBoxmethods (hideDropDownOverlay,hideDropDown) and theThinClientplumbing around them can be removed entirely once the keyboard path is fixed at its source. - [MAJOR] functional
ThinClientalert-box path: The pre-modal and post-modal calls read the globalactiveComboBoxfield twice instead of saving a local. This is safe for anAlertBox(no widgets inside) but breaks the moment the modal can host another combo-box - e.g. if this code path is ever reached for aDialogBoxcontaining its ownCOMBO-BOX. The inner combo-box'sshowDropDown()callssetActiveComboBox(this), clobbering the outer reference. On return frommodalEventLoop,activeComboBoxis eithernull(post-modalhideDropDown()skipped, leaving the outer drop-down wedged in the half-state created byhideDropDownOverlay(): overlay invisible butdropDown != nullanddeferredProgressTriggerstill armed) or pointing at the inner combo-box (we then callhideDropDown()on the wrong widget, and the outer one is still wedged). Additionally, while a dialog-box modal runs, focus/event/wait-state code that consultsdropDown != nullon the outer combo-box gets a lying answer. Save a localComboBox saved = activeComboBox;at entry and operate onsavedboth before and after - though see the[MAJOR] designpoint above: the better resolution is to remove this scaffolding altogether. - [MAJOR] functional
ComboBox.hideDropDownOverlay: Unsafe cast ofdropDownto(DropDownGuiImpl)without aninstanceofcheck. The fielddropDownis typed asDropDown<O>(abstract) and there are two concrete subclasses:DropDownGuiImpl(GUI) andDropDownImpl(CHUI). ThesetActiveComboBoxcall inComboBox.showDropDown()executes for both CHUI and GUI contexts, soactiveComboBoxcan reference a CHUI combo-box whosedropDownis aDropDownImpl. This will throwClassCastExceptionif a message box appears while a CHUI combo-box drop-down is active. Use aninstanceofguard or addhideOverlay()as a no-op on the abstractDropDownbase class. - [MAJOR] style
ComboBox.hideDropDown: Javadoc summary line exceeds 110 characters (117 chars). The line "Hides the active drop-down of the combo-box and prevents the VALUE-CHANGED event after the drop-down is closed." should be wrapped. - [MINOR] style
ComboBox.hideDropDown: Typo in comment: "Do not firevaluechanged after the drop-down eas hidden like that" - missing space in "firevaluechanged" (should be "fire value-changed") and "eas" should be "was". - [MINOR] style
ComboBox: The explicit importcom.goldencode.p2j.ui.client.gui.DropDownGuiImplshould use a wildcard (com.goldencode.p2j.ui.client.gui.*) per coding standards. Note: the pre-existing import forComboBoxGuiImplfrom the same package has the same issue, so the new import follows the existing pattern.
#14 Updated by Razvan-Nicolae Chichirau 2 months ago
- Assignee changed from Delia Mitric to Razvan-Nicolae Chichirau
Replicating the mouse-click behavior for the keyboard selection case is not possible.
On mouse-click:- It is guaranteed that a mouse-click on an item will close the drop-down. So, we can close the drop-down and defer the VALUE-CHANGED processing for later.
- The drop-down should not close. Instead, the VALUE-CHANGED event should propagate and execute its corresponding trigger, while in the drop-down event loop. As such, we can not just defer this event for later because the drop-down should not close in the first place. We must react only if a modal appears while executing this trigger.
What is actually happening¶
- You select an item with the UP/DOWN keyboard keys. This will post a VALUE-CHANGED event.
- The trigger executes and a modal window is invoked. There can really be any modal window, such as alert boxes, printer setup (through
SYSTEM-DIALOG PRINTER-SETUP), modal frame (view-as dialog-box), file-chooser, etc... - The modal event loop starts with processing the OS events first. There is a
WindowActivated (activated = false)on the overlay window which has the drop-down. This will eventually destroy the window and all of its widgets. When the dropdown is destroyed,TC.requestEndCurrentWaitForEventLoop()is invoked because the drop-down thinks it will interrupt its own event loop, but instead it ends the event loop of the modal window. - Now that the modal event loop is terminated, the modal disappears. FWD seems frozen because we are still stuck in the drop-down event loop, even if it was already destroyed.
For testing I'm using the following:
def var cbModalFrame as char view-as combo-box list-items "a", "b", "c", "d" inner-lines 5 label "Modal frame".
def var cbPrinter as char view-as combo-box list-items "a", "b", "c", "d" inner-lines 5 label "Printer".
def var cbAlertBox as char view-as combo-box list-items "a", "b", "c", "d" inner-lines 5 label "Alert-box".
def var cbFileChooser as char view-as combo-box list-items "a", "b", "c", "d" inner-lines 5 label "File chooser".
def frame f cbModalFrame skip(2) cbPrinter skip(2) cbAlertBox skip(2) cbFileChooser with side-labels.
on value-changed of cbModalFrame do:
if cbModalFrame:screen-value = "c" then do:
def var dummycb as char view-as combo-box list-items "1", "2", "3" label "dummy".
def frame modal dummycb with view-as dialog-box.
enable all with frame modal.
wait-for close of frame modal.
end.
end.
on value-changed of cbPrinter do:
if cbPrinter:screen-value = "c" then do:
SYSTEM-DIALOG PRINTER-SETUP.
end.
end.
on value-changed of cbAlertbox do:
if cbAlertbox:screen-value = "c" then do:
message "alert-box" view-as alert-box.
end.
end.
on value-changed of cbFileChooser do:
if cbFileChooser:screen-value = "c" then do:
def var fileName as char.
SYSTEM-DIALOG GET-FILE fileName.
end.
end.
enable all with frame f.
message "Click on any combo-box to activate the drop-down and scroll with the DOWN keyboard key until 'c' is selected. Drop-down should close because of the modal.".
wait-for window-close of current-window.#15 Updated by Razvan-Nicolae Chichirau about 2 months ago
- % Done changed from 90 to 100
- Status changed from WIP to Review
Hynek: Please review 11343a/rev. 16576.
#16 Updated by Hynek Cihlar about 1 month ago
- File combo_dialog.p
added - Status changed from Review to WIP
- % Done changed from 100 to 80
I'm testing the changes and found an issue when opening a dialog-box from the value-changed trigger. See the attached 4gl example. When the dialog-box (or alert-box) is displayed, the event loop of the drop-down must not be already on the call stack.
#17 Updated by Razvan-Nicolae Chichirau about 1 month ago
Is this a conceptual regression? I could not identify any functional regression in your testcase with 11343a. When opening the drop-down and pressing ARROW-DOWN, the drop-down closes and the dialog box is displayed. After exiting the dialog box, the application is neither frozen nor closed.
the event loop of the drop-down must not already be on the call stack.
When navigating with the arrow keys, the drop-down is not supposed to close until a modal dialog is shown. As such, the mouse-click behavior cannot be replicated, since a mouse click guarantees that the drop-down exits first.
In your example, the combo-box value is changed using the keyboard, which triggers a VALUE-CHANGED event. A modal dialog is then invoked from this trigger while the drop-down WAIT-FOR loop is already on the call stack. This is the first point at which we can reliably determine that the drop-down should be closed, but by then it is too late to do so directly. We cannot simply abort the current event processing to terminate the drop-down loop and then get back to the trigger processing.
Instead, we recognize and flag this situation. The flag causes the termination of the current WAIT-FOR to be deferred, which in this case happens to be the dialog-box loop. Once the VALUE-CHANGED trigger completes, the pending END condition is processed.
#18 Updated by Razvan-Nicolae Chichirau 14 days ago
- % Done changed from 80 to 100
- Status changed from WIP to Review
I'll get this back to the review phase to get a response from Hynek for #11343-17.