Project

General

Profile

Bug #8673

Deduce the logic behind OE focus management and implement in FWD

Added by Vladimir Tsichevski 10 days ago. Updated about 16 hours ago.

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

20%

billable:
No
vendor_id:
GCD
case_num:

8376-with-focusable.p Magnifier (5.53 KB) Vladimir Tsichevski, 04/24/2024 08:54 AM


Related issues

Related to User Interface - Bug #8376: Focus transfer problem with frames having no focusable contents New
Related to User Interface - Bug #8095: LEAVE trigger fires in no focus change situation New
Related to User Interface - Bug #7786: FOCUS handle must return only field-level widgets New
Related to User Interface - Bug #7685: LEAVE/ENTRY events are raised before MOUSE-UP event. New 08/14/2023
Related to User Interface - Bug #7684: ENTRY event is emitted twice New 08/14/2023
Related to User Interface - Support #7339: UI - Focus New
Related to User Interface - Bug #8711: TAB-STOP attribute wrong implementation for RECTANGLE, IMAGE and TEXT widgets New

History

#1 Updated by Vladimir Tsichevski 10 days ago

There is a feature related to how OE manages focus I cannot understand.

I wrote the 8376-with-focusable.p example to learn focus-related stuff.

In this example there is a number of frames, most of them nested, which contain a number of buttons.

When I click with mouse in any frame, the first button in this frame is focused.

But there is a single exception: the frame named outerFrame has two enabled buttons, but clicking inside this frame does not move focus to one of the frame buttons.

I am stuck, since I cannot find out, why is this frame so special.
Probably, I am missing something obvious?

The logic behind this feature should be discovered in order to be implemented in FWD.

#3 Updated by Vladimir Tsichevski 4 days ago

  • Status changed from New to WIP
  • % Done changed from 0 to 20

When the target is a frame, and there is no focus hint set in this frame (something roughly similar to the AbstractContainer#current field in FWD),
OE searches for the suitable field in its field group. This is known.

The new hypothesis: the search is aborted on the first non-field item (a frame) found in the field group.

In the 8376-with-focusable.p example the outerFrame is enabled with the following statement:

ENABLE focusable4 focusable6 WITH FRAME outerFrame.

This way results in that the focusable4 and focusable6 buttons move to the end of the field group widgets, and the innerFrame is the first item in the field group.

So, when the user click the outerFrame with mouse the search for the child suitable as focus target, aborts on the innerFrame, and the outerFrame stays as the focus target.

#4 Updated by Vladimir Tsichevski 4 days ago

  • Related to Bug #8376: Focus transfer problem with frames having no focusable contents added

#5 Updated by Vladimir Tsichevski 4 days ago

  • Related to Bug #8095: LEAVE trigger fires in no focus change situation added

#6 Updated by Vladimir Tsichevski 4 days ago

  • Related to Bug #7786: FOCUS handle must return only field-level widgets added

#7 Updated by Vladimir Tsichevski 4 days ago

  • Related to Bug #7685: LEAVE/ENTRY events are raised before MOUSE-UP event. added

#8 Updated by Vladimir Tsichevski 4 days ago

  • Related to Bug #7684: ENTRY event is emitted twice added

#9 Updated by Vladimir Tsichevski 3 days ago

#10 Updated by Vladimir Tsichevski 3 days ago

  • Related to Bug #8711: TAB-STOP attribute wrong implementation for RECTANGLE, IMAGE and TEXT widgets added

#11 Updated by Vladimir Tsichevski 3 days ago

This comment is reserved for the emerging documentation describing the logic behind OE focus management.

It will be edited later.

#12 Updated by Vladimir Tsichevski about 21 hours ago

Question to gurus:

A Progress trigger can return a value. If the NO-APPLY is returned, the further event processing is aborted.

The question: are values other than the NO-APPLY ever used by Progress?

#13 Updated by Greg Shah about 19 hours ago

First, I want to record some of our older documentation:

https://proj.goldencode.com/artifacts/javadoc/latest/api/com/goldencode/p2j/ui/chui/package-summary.html#Focus_Management
https://proj.goldencode.com/artifacts/javadoc/latest/api/com/goldencode/p2j/convert/package-summary.html#Transaction_Processing_and_Block

The first reference is not 100% complete. It especially was only looked at in ChUI and the influence of mouse processing, drag and drop, multiple windows and child frames are not considered.

The second reference is based on 9.x and thus is not up to date with the additional block processing of OO, structured error handling or the more recent transaction processing related changes in 10.x and 11.x. It also doesn't consider anything related to appserver/REST/SOAP agent session processing, nor anything about persistent/super procedures.

Even with these caveats, these are useful resources.

The question: are values other than the NO-APPLY ever used by Progress?

Limiting this question to triggers, the answer is yes and no.

This may help (it is the javadoc from BlockManager.returnWorker()):

Return from the nearest enclosing top-level block. This will process differently based on the type of the top-level block (internal/external procedures, triggers or user-defined functions), the type of return processing that is requested (see BlockManager.ReturnType) and the presence or absence of a value to return.

The following behavior is provided:

 Codes   Meaning                
 ------  -----------------------------------------------------
 C       Invoke LogicalTerminal.consume()
 E       Invoke TransactionManager.triggerErrorInCaller()
 V       Invoke ControlFlowOps.setReturnValue(val)
 N       Invoke ControlFlowOps.setReturnValue("")
 F       Invoke setFuncReturn(val)
 U       Invoke setFuncReturn(null) (set return value as unknown)
 I       Invalid (compile error in Progress)

  Return    Return    Internal  External  Trigger  Function
  Value      Type       Proc      Proc
 --------  ---------  --------  --------  -------  --------
 null      NORMAL     N         N         N        U
 non-null  NORMAL     V         V         V        F
 null      CONSUME    N         N         NC       U
 non-null  CONSUME    V         V         VC       F
 null      ERROR      NE        NE        I        U
 non-null  ERROR      VE        VE        I        U

In all cases, a ReturnUnwindException will be thrown and this will cause the stack to unwind to the nearest enclosing top-level block. That block will then handle the return in a block specific manner. For example, in a user-defined function, the given value or by default the unknown value will be returned to the caller. Procedures and triggers have void return values.

The part about the ReturnUnwindException is no longer 100% correct but otherwise the details are quite accurate and match the 4GL behavior. The way to interpret (for triggers) is as follows:

  • A RETURN. ("NORMAL" return without a value) in a trigger sets the RETURN-VALUE to "" and then returns, allowing default processing to occur.
  • A RETURN <expression>. ("NORMAL" return with a value) in a trigger sets the RETURN-VALUE to the evaluated value of the <expression> and then returns, allowing default processing to occur.
  • A RETURN NO-APPLY. ("CONSUME" return without a value) in a trigger sets the RETURN-VALUE to "" and then returns, suppressing the default processing.
  • A RETURN NO-APPLY <expression>. ("CONSUME" return with a value) in a trigger sets the RETURN-VALUE to the evaluated value of the <expression> and then returns, suppressing the default processing.
  • A RETURN ERROR. ("ERROR" return without a value) in a trigger causes a compilation error.
  • A RETURN ERROR <expression>. ("ERROR" return with a value) in a trigger causes a compilation error.

So these other constructions can affect the behavior but in no case does the value actually get returned as in a function call. In the "NORMAL" return with a value and "CONSUME" return with a value, the value can only be found in RETURN-VALUE. Relying upon this is tricky at best, because of the very idea of a trigger. It is happening in response to an event occurring, so how do you check the RETURN-VALUE just after a trigger executes BUT BEFORE any other procedure or trigger processing overwrites the RETURN-VALUE. Since there is just one global RETURN-VALUE, it is a pretty poor idea to use it from a trigger, in my opinion.

I hope this helps.

Marian: If I've gotten anything wrong here, please do let us know.

#14 Updated by Vladimir Tsichevski about 17 hours ago

Thanks, Greg.

One question:

The following two RETURN <expression>. statements look the same:

  • A RETURN <expression>. ("NORMAL" return with a value) in a trigger sets the RETURN-VALUE to the evaluated value of the <expression> and then returns, allowing default processing to occur.
  • A RETURN <expression>. ("CONSUME" return with a value) in a trigger sets the RETURN-VALUE to the evaluated value of the <expression> and then returns, suppressing the default processing.

How can the "CONSUME" return with a value be expressed in 4gl?

#15 Updated by Greg Shah about 16 hours ago

I edited it. It should have been RETURN NO-APPLY <expression>..

Also available in: Atom PDF