Antonio: ALT accelerator with button - Urgent help needed

Post Reply
Colin Wisbey
Posts: 56
Joined: Mon Jul 03, 2006 2:34 am

Antonio: ALT accelerator with button - Urgent help needed

Post 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
User avatar
Antonio Linares
Site Admin
Posts: 37481
Joined: Thu Oct 06, 2005 5:47 pm
Location: Spain
Contact:

Post by Antonio Linares »

Colin,

We are already reviewing your example to provide you an answer as soon as possible
regards, saludos

Antonio Linares
www.fivetechsoft.com
User avatar
Antonio Linares
Site Admin
Posts: 37481
Joined: Thu Oct 06, 2005 5:47 pm
Location: Spain
Contact:

Post 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.
regards, saludos

Antonio Linares
www.fivetechsoft.com
User avatar
Antonio Linares
Site Admin
Posts: 37481
Joined: Thu Oct 06, 2005 5:47 pm
Location: Spain
Contact:

Post 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
regards, saludos

Antonio Linares
www.fivetechsoft.com
User avatar
Antonio Linares
Site Admin
Posts: 37481
Joined: Thu Oct 06, 2005 5:47 pm
Location: Spain
Contact:

Post by Antonio Linares »

Same behaviors with xHarbour and FWH 8.07
regards, saludos

Antonio Linares
www.fivetechsoft.com
Colin Wisbey
Posts: 56
Joined: Mon Jul 03, 2006 2:34 am

Post 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
Colin Wisbey
Posts: 56
Joined: Mon Jul 03, 2006 2:34 am

Post by Colin Wisbey »

Oops!. I referred to "ALT A".

I meant "ALT T" of course.
User avatar
Antonio Linares
Site Admin
Posts: 37481
Joined: Thu Oct 06, 2005 5:47 pm
Location: Spain
Contact:

Post 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() ) 
regards, saludos

Antonio Linares
www.fivetechsoft.com
Colin Wisbey
Posts: 56
Joined: Mon Jul 03, 2006 2:34 am

Post 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
Colin Wisbey
Posts: 56
Joined: Mon Jul 03, 2006 2:34 am

Post 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.)
User avatar
Antonio Linares
Site Admin
Posts: 37481
Joined: Thu Oct 06, 2005 5:47 pm
Location: Spain
Contact:

Post 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.
regards, saludos

Antonio Linares
www.fivetechsoft.com
Colin Wisbey
Posts: 56
Joined: Mon Jul 03, 2006 2:34 am

Post 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
User avatar
Antonio Linares
Site Admin
Posts: 37481
Joined: Thu Oct 06, 2005 5:47 pm
Location: Spain
Contact:

Post 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!
regards, saludos

Antonio Linares
www.fivetechsoft.com
Post Reply