Bug #8673
Deduce the logic behind OE focus management and implement in FWD
30%
Related issues
History
#1 Updated by Vladimir Tsichevski 2 months ago
- File 8376-with-focusable.p
added
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 2 months 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 2 months ago
- Related to Bug #8376: Focus transfer problem with frames having no focusable contents added
#5 Updated by Vladimir Tsichevski 2 months ago
- Related to Bug #8095: LEAVE trigger fires in no focus change situation added
#6 Updated by Vladimir Tsichevski 2 months ago
- Related to Bug #7786: FOCUS handle must return only field-level widgets added
#7 Updated by Vladimir Tsichevski 2 months ago
- Related to Bug #7685: LEAVE/ENTRY events are raised before MOUSE-UP event. added
#8 Updated by Vladimir Tsichevski 2 months ago
- Related to Bug #7684: ENTRY event is emitted twice added
#9 Updated by Vladimir Tsichevski 2 months ago
- Related to Support #7339: UI - Focus added
#10 Updated by Vladimir Tsichevski 2 months ago
- Related to Bug #8711: TAB-STOP attribute wrong implementation for RECTANGLE, IMAGE and TEXT widgets added
#11 Updated by Vladimir Tsichevski 2 months ago
Focus data model¶
Focus state consists of the following data:
Window.focus
Field-level focused widget, which receives keyboard input, initiallynull
.
The OE FOCUS handle contains the value of this field for the currently focused window.
Window.rawFocus
.
The last widget the user selected by mouse. Initiallynull
. Can be field-level or frame. This field cannot be directly accessed from 4gl.
Frame.currentFocus
The preferred FOCUS candidate field-level.
This field is eithernull
or points to one of field-level members of the frame field group, which was last selected forWindow.focus
.
Focus change by mouse events control flow¶
Focus state can be affected by MOUSE DOWN and MOUSE UP actions.
The control flow looks as follows:
- Decide if this mouse type/button combination can cause focus-related actions, return if not.
- Calculate the effective target focus (which can be either a field-level or a frame).
- If the resulting target focus is the same as the current
Window.rawFocus
value, then return, do nothing. - Fire necessary
LEAVE/ENTRY
event triggers. - If no trigger returned
NO_APPLY
: # If the target focus is a field-level, assign it toWindow.focus
and updateFrame.currentFocus
for the field frame. - Save the target focus to
Window.rawFocus
.
Which mouse event may initiate a focus change¶
Warning: this section describes the FWD implementation, the information needs probably be checked with original OE.
- The mouse button is the LEFT button for any widget or the mouse button is the RIGHT button for any widgets, except the following FWD widget classes:
BorderedPanelGuiImpl
EditorGuiImpl
FillInGuiImpl
ScrollbarGuiButton
ScrollbarGuiImpl
- event is one of
UP
,DOWN
orCLICK
. - widget is any, except:
ScrollbarGuiButton
ScrollbarGuiImpl
Effective target focus calculation¶
- Initially use the event source as the target focus.
- If the target focus is a widget which cannot be a target focus (either a disabled field-level, or a widget which by its nature does not accept keyboard input), then assign widget's frame to target focus.
- If target focus is not a frame, return the target focus value
- If the
Frame.currentFocus
is a field-level, then return theFrame.currentFocus
value - Search the target focus field group tab ring for a suitable field: search until either an enabled field-level found, and we use it as the new target, or a frame found, which aborts the search.
Fire LEAVE/ENTRY event triggers¶
Given a target focus, fire ENTRY
and LEAVE
events. Abort the operation if any trigger returns NO-APPLY
.
- If target focus differs from the
Window.rawFocus
:- If
Window.rawFocus
is set:- Fire
LEAVE
trigger forWindow.rawFocus
- If
Window.rawFocus
is a field-level:- If target focus is a field-level:
- If the frame of the target focus differs from the frame of
Window.rawFocus
:- fire
LEAVE
trigger for the frame ofWindow.rawFocus
- if the
Frame.currentFocus
field of the frame of the target focus is set or theFrame.currentFocus
field of the frame of the target focus differs from the target focus:- fire
ENTRY
trigger for the frame of the target focus
- fire
- fire
- If the frame of the target focus differs from the frame of
- Otherwise (target focus is a frame):
- fire
LEAVE
trigger for the frame ofWindow.rawFocus
- fire
- If target focus is a field-level:
- Otherwise (
Window.rawFocus
is a frame):- If target focus is field-level and the frame of the target focus differs from
Window.rawFocus
and (the frame of the target focus.currentFocus is not set
or theFrame.currentFocus
field of the frame of the target focus differs from the target focus):- fire
ENTRY
trigger for the frame of the target focus
- fire
- If target focus is field-level and the frame of the target focus differs from
- Fire
- Otherwise (
Window.rawFocus
is not set):- if target focus is a field-level and (
Frame.currentFocus
field of the frame of the target focus is not set
orFrame.currentFocus
field of the frame of the target focus differs from target focus): # fireENTRY
trigger for the frame of the target focus
- if target focus is a field-level and (
- If
- fire
ENTRY
trigger for the target focus
#12 Updated by Vladimir Tsichevski 2 months 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 2 months 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 UIn 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 theRETURN-VALUE
to""
and then returns, allowing default processing to occur. - A
RETURN <expression>.
("NORMAL" return with a value) in a trigger sets theRETURN-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 theRETURN-VALUE
to""
and then returns, suppressing the default processing. - A
RETURN NO-APPLY <expression>.
("CONSUME" return with a value) in a trigger sets theRETURN-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 2 months 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 theRETURN-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 theRETURN-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?
#16 Updated by Vladimir Tsichevski about 2 months ago
- File 8376-with-focusable.p
added
- File functions.i added
The #8673-11 now contains the focus data model and the focus change by mouse flow. Please, comment.
The demo program used to test the model is 8376-with-focusable.p
(with the include file functions.i
).
#17 Updated by Vladimir Tsichevski about 2 months ago
- % Done changed from 20 to 30
#18 Updated by Vladimir Tsichevski about 2 months ago
- File printEntryLeave.i added
The missing printEntryLeave.i
required to run the 8376-with-focusable.p
uploaded.
#19 Updated by Vladimir Tsichevski about 2 months ago
- Related to Bug #8770: ENTRY event is not sent for a frame when window receives focus added
#20 Updated by Vladimir Tsichevski about 1 month ago
- Related to Bug #8809: Incorrect next enabled widget calculation added
#21 Updated by Vladimir Tsichevski about 1 month ago
- Related to Bug #8586: Fields are not in FIELD-GROUP until they are enabled added
#22 Updated by Vladimir Tsichevski 21 days ago
A strange thing in OE.
The test application:
- Defines a frame
outerFrame
with a buttonfocusable4
. - The button
ENTRY
trigger returnsNO-APPLY
, so the button cannot be selected for focus.
When the application starts, and the window receives focus for the first time, the order of ENTRY
events is as this:
focusable4
outerFrame
DEFAULT-WINDOW
When the window receives focus next times, the order of events is different:
outerFrame
focusable4
DEFAULT-WINDOW
I wonder why is this, and shall we implement this behavior verbatim?
#24 Updated by Vladimir Tsichevski 20 days ago
- Related to Bug #8872: WAIT-FOR with FOCUS explicitly specified: NO-APPLY return from ENTRY trigger must be ignored added
#25 Updated by Vladimir Tsichevski 5 days ago
- Related to Bug #8909: WAIT-FOR with PAUSE and no explicit focus: no default focus resolution should be done added
#26 Updated by Vladimir Tsichevski 4 days ago
- Related to Bug #8911: Default focus resolution should NOT be done until window receives focus added
#27 Updated by Vladimir Tsichevski 4 days ago
OE focus management feature I cannot explain: the default focus resolution differs in the DEFAULT-WINDOW
and dynamic windows.
- in the
DEFAULT-WINDOW
, if the root frame has no focusable fields, inner frames are searched for such. - in a dynamically created window, if the root frame has no focusable fields, no further search happens.
Can anyone provide me with any hints?
#28 Updated by Vladimir Tsichevski about 9 hours ago
- Related to Bug #8917: Mouse focus management: focus update must happen on mouse down, not on mouse up added