Page 1 of 1
Antonio: ALT accelerator with button - Urgent help needed
Posted: Wed Aug 13, 2008 7:56 am
by Colin Wisbey
FUNCTION TESTALT
// Indicates differences between results when clicking a button
// compared to instead using ALT accelerator for the button.
// Example 1. BUTTON'S ACTION DOESN'T FURTHER CHANGE THE FOCUS:
// If ALT accelerator is pressed for a button when the current focus
// is on a GET which has a valid, focus returns to the control (in
// this case a GET) that had focus before the button's action was
// executed - and the valid is executed twice.
// If instead the button is clicked, focus remains on the BUTTON after
// the button's action is executed - and the valid hass executed just
// once.
//
// 2. BUTTON'S ACTION DOES FURTHER CHANGE THE FOCUS:
// If ALT accelerator is pressed for a button when the current focus
// is on a GET which has a valid, focus returns to the control (in
// this case a GET) that had focus before the button's action was
// executed - and the valid is not executed until AFTER the button's
// ACTION.
// If instead the button is clicked, focus remains on the BUTTON after
// the button's action is executed - and the valid is executed BEFORE
// the button's ACTION.
//--------------------------------------------
#include 'Fivewin.ch'
LOCAL oDlg
LOCAL oGet1, oGet2, oGet3, oBtn
LOCAL cVar1 := 'ABCDEF '
LOCAL cVar2 := SPACE(8)
LOCAL cVar3 := SPACE(8)
DEFINE DIALOG oDlg FROM 0, 0 TO 10, 30 ;
TITLE 'Test Alt / Button / GET_VALID'
@ 1, 1 GET oGet1 VAR cVar1 SIZE 30,12 PIXEL ;
Valid (MSGBEEP(), .T. )
@ 2, 1 GET oGet2 VAR cVar2 SIZE 30,12
@ 3, 1 GET oGet3 VAR cVar3 SIZE 30,12
// Example 1: ACTION doesn't try to further change focus:
@ 40,50 BUTTON oBtn PROMPT '&Test' ;
SIZE 30, 20 PIXEL ;
ACTION (SYSWAIT(1), ( cVar2 := cVar1 ), oGet2:Refresh())
// Example 2: Next ACTION does try to further change focus.
// Uncomment to see the difference:
/*
@ 40,50 BUTTON oBtn PROMPT '&Test' ;
SIZE 30, 20 PIXEL ;
ACTION MsgInfo('This is a test')
*/
ACTIVATE DIALOG oDlg CENTER
return NIL
//-------------------------------------------------
Hope you can help fix if a bug, or tell me what I need to do.
Thanks,
Colin Wisbey
Posted: Wed Aug 13, 2008 8:41 am
by Antonio Linares
Colin,
We are already reviewing your example to provide you an answer as soon as possible
Posted: Wed Aug 13, 2008 9:01 am
by Antonio Linares
Colin,
If you modify your code this way, then there is no difference between using the mouse to click on the button or using the accelerator. Please review the generated .TXT files contents:
Code: Select all
@ 1, 1 GET oGet1 VAR cVar1 SIZE 30,12 PIXEL ;
VALID ( LogFile( "valid.txt", { "valid" } ), .T. )
...
@ 40,50 BUTTON oBtn PROMPT '&Test' ;
SIZE 30, 20 PIXEL ;
ACTION ( LogFile( "action.txt", { "action" } ), ( cVar2 := cVar1 ), oGet2:Refresh() )
Please don't use MsgInfo() as it changes the focus sequence.
Posted: Wed Aug 13, 2008 9:09 am
by Antonio Linares
This code seems to be fine also. Please check the contents of the generated TXT files. Even using MsgInfo():
Code: Select all
@ 1, 1 GET oGet1 VAR cVar1 SIZE 30,12 PIXEL ;
Valid ( LogFile( "valid.txt", { "valid" } ), .T. )
...
@ 40,50 BUTTON oBtn PROMPT '&Test' ;
SIZE 30, 20 PIXEL ;
ACTION ( LogFile( "action.txt", { "action" } ), MsgInfo( "action" ) )
Tested with Harbour and FWH 8.07
Posted: Wed Aug 13, 2008 9:10 am
by Antonio Linares
Same behaviors with xHarbour and FWH 8.07
Posted: Wed Aug 13, 2008 10:52 am
by Colin Wisbey
Thanks for prompt reply, Antonio, but it doesn't address either of the 2 problems.
Please try the following with my original example #1:
Step 1: Place cursor in oGet1.
Step 2: Action the button by using ALT A. Notice that focus is still on oGet1. When done, click on another get. The VALID (which you can hear from the Msgbeep()) gets evaluated twice.
Step 3: Place cursor in oGet1 again.
Step 2: Action the button by clicking the button. Notice that focus is now on the button, not on oGet1. When done, click on another get. The VALID (which you can hear from the Msgbeep()) got evaluated prior (and only) prior) to the button click, which is how I want a button to be actioned.
For problem 2, try the above with my original example #2.
The example I posted is an artificial example to demonstrate the problem. In my real apps, the equivalent of my oGet1 VALID is vital in both it needing to be executed only once and before the button's action gets executed. The VALID can't afford to executed twice or only after the button's action.
In short, clicking on the button works the way it always has and works perfectly. However, if we instead action the button by ALT-accelerator, the results are not only different (depending on what the VALID does) but very different in change of focus and when the VALID gets actioned.
It's never previously presented a problem for my users because I haven't offered ALT accelerators with a button until now. Now they are screaming at me due to reproducible errors whenever they use the ALT accelerator.
Incidentally, I used MSGINFO() in my 2nd example as it represents the equivalent of real-world actions of my buttons. e.g. The real-world dialog represents a docket (invoice) and one of the buttons calls up the image of the docket (obviously changing focus in the process, as does Msginfo()). The VALID on the GET does the equivalent of incrementing a base number so I can't afford it to be actioned twice.
As I said, clicking on a button works fine. The problem is with actioning the button by ALT accelerator.
Hope that explains my problems better than my original post did.
Thanks again. Hope you can help.
Col
Posted: Wed Aug 13, 2008 11:16 am
by Colin Wisbey
Oops!. I referred to "ALT A".
I meant "ALT T" of course.
Posted: Wed Aug 13, 2008 3:06 pm
by Antonio Linares
Colin,
When you press a button accelerator, the focus is not changed to the button. Just its action is fired. A workaround for this:
Code: Select all
@ 1, 1 GET oGet1 VAR cVar1 SIZE 30,12 PIXEL ;
Valid ( MsgBeep(), .T. )
...
@ 40,50 BUTTON oBtn PROMPT '&Test' ;
SIZE 30, 20 PIXEL ;
ACTION ( ( cVar2 := cVar1 ), oGet2:Refresh(), oBtn:SetFocus() )
Posted: Wed Aug 13, 2008 3:58 pm
by Colin Wisbey
>When you press a button accelerator, the focus is not changed to the button. Just its action is fired.
Correct. That's the problem I've been trying to explain.
>A workaround for this
< snip >
Great. That should do it. That solution never crossed my mind (but probably should have, now I've seen how simple it is).
Many thanks, Antonio. I really appreciate such prompt and useful support.
Col
Posted: Thu Aug 14, 2008 2:42 am
by Colin Wisbey
Antonio, your suggestion fix doesn't make any difference.
The only difference it makes is that, if using ALT accelerator, focus now finishes up on the button. However, using ALT accelerator, the valid still gets executed as per all the original problems (wrong timing and/or executed twice) in my 2 examples.
Any other thoughts?
From a quick check, it seems Microsoft's apps behave identically regardless of whether button action is initiated by clicking or by ALT accelerator. (i.e. the way clicking on a button in FWH works, the focus automatically finishing on the button control). This is the way FWH should automatically work. Whether clicking on a button or using ALT accelerator, the sequence of actions and end results need to be identical. After all, the ALT accelerator is intended to be merely an alternative equivalent to clicking the button.
(I sincerely appreciate the time you are investing on this problem. The problem poses a potential trap for any developer using ALT accelerators with buttons, especially if VALID clauses are involved.)
Posted: Thu Aug 14, 2008 6:41 am
by Antonio Linares
Colin,
What FWH version are you using ?
Here its working fine using FWH 8.07. If you use LogFile() as I shown here then you will see that the VALID is executed just once.
Posted: Thu Aug 14, 2008 10:22 am
by Colin Wisbey
Antonio, please try this sample. Note that a single Validxxxxx txt file gets created (as it should) if we click the button but two Validxxxxx txt files get created if we use ALT accelerator.
//--------------------------------------
FUNCTION TESTALT
#include 'Fivewin.ch'
LOCAL oDlg
LOCAL oGet1, oGet2, oGet3, oBtn
LOCAL cVar1 := 'ABCDEF '
LOCAL cVar2 := SPACE(8)
LOCAL cVar3 := SPACE(8)
DEFINE DIALOG oDlg FROM 0, 0 TO 10, 30 ;
TITLE 'Test Alt / Button / GET_VALID'
@ 1, 1 GET oGet1 VAR cVar1 SIZE 30,12 PIXEL ;
Valid ( LogFile( "valid" +STR(Seconds(),9,2) +".txt", { "valid" } ), .T. )
@ 2, 1 GET oGet2 VAR cVar2 SIZE 30,12
@ 3, 1 GET oGet3 VAR cVar3 SIZE 30,12
@ 40,50 BUTTON oBtn PROMPT '&Test' ;
SIZE 30, 20 PIXEL ;
ACTION ( ( SYSWAIT(2),;
LogFile( "action" +STR(Seconds(),9,2) +".txt", { "action" } ),;
oBtn:SetFocus() ) )
ACTIVATE DIALOG oDlg CENTER
return NIL
//----------------------------------
FWH 8.06
Thanks
Col
Posted: Thu Aug 14, 2008 10:20 pm
by Antonio Linares
Colin,
Many thanks for your example. Yes, you were right and there was a problem that was firing twice the focus, when Alt + acc was pressed.
This change is required in Class TButton:
Code: Select all
METHOD Click() CLASS TButton
if ! ::lProcessing
::lProcessing = .t.
if ::bWhen != nil .and. ! Eval( ::bWhen )
::lProcessing = .f.
return nil
endif
::oWnd:lValidating = .T.
if GetFocus() != ::hWnd
SetFocus( ::hWnd )
endif
if ::bAction != nil
Eval( ::bAction )
endif
if ! ::lCancel .and. ::oWnd:hWnd != 0
Super:Click()
endif
if ( GetFocus() == ::hWnd )
::PostMsg( BM_SETSTYLE, BS_DEFPUSHBUTTON, 1 )
else
if ! ::lDefault
::PostMsg( BM_SETSTYLE, BS_PUSHBUTTON, 1 )
endif
endif
::lProcessing = .f.
endif
return nil
With such change, this test is properly working here:
Code: Select all
#include 'Fivewin.ch'
function Main()
LOCAL oDlg
LOCAL oGet1, oGet2, oGet3, oBtn
LOCAL cVar1 := 'ABCDEF '
LOCAL cVar2 := SPACE( 8 )
LOCAL cVar3 := SPACE( 8 )
DEFINE DIALOG oDlg FROM 0, 0 TO 10, 30 ;
TITLE 'Test Alt / Button / GET_VALID'
@ 1, 1 GET oGet1 VAR cVar1 SIZE 30,12 PIXEL ;
Valid ( LogFile( "valid" + AllTrim( Str( GetTickCount() ) ) + ".txt", { "valid" } ), .T. )
@ 2, 1 GET oGet2 VAR cVar2 SIZE 30,12
@ 3, 1 GET oGet3 VAR cVar3 SIZE 30,12
@ 40,50 BUTTON oBtn PROMPT '&Test' ;
SIZE 30, 20 PIXEL ;
ACTION ( LogFile( "action" + AllTrim( Str( GetTickCount() ) ) + ".txt", { "action", GetClassName( GetFocus() ) } ) )
ACTIVATE DIALOG oDlg CENTER
return NIL
Please test it and let us know your results. Thanks!