16 Replies Latest reply on Apr 9, 2011 5:57 AM by Peter Kahrel

    [CS4/CS5] ScriptUI focus event (Win/Mac)

    Marc Autret Level 4

      Hi friends,

       

      I'm looking after a way to control the focus of EditText widgets within a Dialog but I'm totally confused by the event loop logics —especially in Mac OS.

       

      The basic task I need to achieve is to attach a validation mechanism to an EditText. When the user has entered a wrong field and the change event occurs, I want to display an alert box, then re-activate the field. The problem is that both the change event and the alert box interfer with focus attribution. I think that user or system focus/blur events are stacked so that any command that attempts to focus back to the original widget cannot work while the stack is not empty.

       

      Here is a simple test to illustrate my problem. The script is supposed to display an alert when the user change a field, then it tries to focus back to the corresponding field through myWidget.active = true. This does not work.

       

      var     u,
           w = new Window('dialog',u,u,{xname:'Window'}),
           e1 = w.add('edittext', u, "",{xname:'EditText (1)'}),
           e2 = w.add('edittext', u, "",{xname:'EditText (2)'}),
           eInfo = w.add('edittext', [0,0,300,400], "",{xname: 'EditText (Info)', multiline:true}),
           b = w.add('button',u,'Clear', {xname:'Button'});
      
      e1.characters = e2.characters = 20;
      
      var anyEvent = function(ev)
           {
           var tg = ev.target;
           eInfo.text += (tg.properties.xname + ' -> ' +
                ev.type.toUpperCase() +
                (tg.active ? '  (active)':'  (non active)') +
                '\n');
           };
      
      var changeEvent = function(ev)
           {
           eInfo.text += ('\n--- BEFORE Breaking Alert\n');
           alert("Breaking alert");
           
           // Trying to FOCUS back on ev.target
           // ---
           eInfo.text += ('\n--- BEFORE Active=true\n');
           ev.target.active = true;
           eInfo.text += ('--- AFTER Active=true\n');
           };
                
      // Event 'inspector'
      // ---
      w.addEventListener('change', anyEvent);
      w.addEventListener('focus', anyEvent);
      w.addEventListener('blur', anyEvent);
      
      // Events
      // ---
      w.addEventListener('change', changeEvent);
      b.onClick = function(){eInfo.text = '';};
      
      w.show();
      

       

      I tried various strategies to address this problem by using event timestamps and/or dispatching custom new UIEvent('focus'...), but nothing works conveniently.

       

      In addition I got odd behaviours in Mac OS. The below screenshot shows a piece of a ScriptUI dialog —using a custom framework, so don't be surprised by the skin! What is weird is that in a particular state *two* EditText controls have the focus ring at the same time:

       

      Mac-double-focus.png

       

      I didn't know such a thing could happen. I probably did not understand anything about the 'focus' paradigm.

       

      Any help from an expert would be greatly appreciated. Thanks a lot!

       

      @+

      Marc

        • 1. Re: [CS4/CS5] ScriptUI focus event (Win/Mac)
          Marijan Tompa [tomaxxi] Level 4

          Hey Marc,

           

          It's obvious that is something wrong with event listeners

          and also alert is messing things up too!

          Also I tried to remove alert from script and it didn't worked either.

           

          I tried this quickly, and it works,

          but you will notice that "validation error" is showed multiple times...

           

          var     u,
               w = new Window('dialog',u,u,{xname:'Window'}),
               e1 = w.add('edittext', u, "",{xname:'EditText (1)'}),
               e2 = w.add('edittext', u, "",{xname:'EditText (2)'}),
               eInfo = w.add('edittext', [0,0,300,400], "",{xname: 'EditText (Info)', multiline:true}),
               b = w.add('button',u,'Clear', {xname:'Button'});
           
          e1.characters = e2.characters = 20;
          
          e1.onDeactivate = function(){
              if((isNaN(e1.text))){
                  e1.active = true;
                  eInfo.text += "--- Validation Error\n";
              }
          }
          
          e2.onDeactivate = function(){
              if((isNaN(e2.text))){
                  e2.active = true;
                  eInfo.text += "--- Validation Error\n";
              }
          }
          
          w.show();
          

           

          Hope this helps just a little bit!

           

          --

          Marijan (tomaxxi)

          1 person found this helpful
          • 2. Re: [CS4/CS5] ScriptUI focus event (Win/Mac)
            Marc Autret Level 4

            Thanks Marijan.

             

            OK your code works. I already experienced that using myWidget.active = true within a blur event callback —or through the onDeactivate shortcut— prevents the control from losing the focus. But if you add the alert('Breaking alert'); line within your validation block you get an infinite alert loop :-(

             

            The fact that "validation error" is showed multiple times proves that ScriptUI internally dispatch the blur event multiple times, which means that it does not  immediately cancel the user interaction. And AFAIK the blur event is not cancelable via preventDefault().

             

            That's all the problem. Is there a way to 'kill' the event stack so as to prevent any side effect?

            What I'm looking for is a clean solution that maybe ScriptUI does not allow...

             

            Thanks again,

            Marc

            • 3. Re: [CS4/CS5] ScriptUI focus event (Win/Mac)
              Harbs. Level 6

              H Marc,

               

              I haven't read your post very well, but Bob Stucky put together some validation scripts some time back. Have you looked at them?

               

              Harbs

              1 person found this helpful
              • 4. Re: [CS4/CS5] ScriptUI focus event (Win/Mac)
                Marc Autret Level 4

                Hi Harbs,

                 

                Of course I did :-) Its validation components are very powerful and I used them a few years ago. Today I have my own library with more specific features. I don't remember whether Bob's approach works around the focus problem, especially when one throws an alert over the dialog...

                 

                Anyway I'm convinced Bob has outcomes to the hardest ScriptUI problems, so I hope he will come around.

                 

                @+

                Marc

                • 5. Re: [CS4/CS5] ScriptUI focus event (Win/Mac)
                  Marijan Tompa [tomaxxi] Level 4

                  Silly but it's working:

                   

                  var     u,
                      p,
                       w = new Window('dialog',u,u,{xname:'Window'}),
                       e1 = w.add('edittext', u, "",{xname:'EditText (1)'}),
                       e2 = w.add('edittext', u, "",{xname:'EditText (2)'}),
                       eInfo = w.add('edittext', [0,0,300,400], "",{xname: 'EditText (Info)', multiline:true}),
                       b = w.add('button',u,'Clear', {xname:'Button'});
                   
                  e1.characters = e2.characters = 20;
                   
                  e1.onDeactivate = function(){
                      if((isNaN(e1.text))){
                          e1.active = true;
                          eInfo.text += "--- Validation Error\n";
                          if(p == "" ){
                              p = this.properties.xname;
                              alert("Validation Error");
                          }
                      }
                  }
                  
                  e1.onChanging = function(){
                      p = "";
                  }
                  
                  e2.onChanging = function(){
                      p = "";
                  }
                  
                  e2.onDeactivate = function(){
                      if((isNaN(e2.text))){
                          e2.active = true;
                          eInfo.text += "--- Validation Error\n";
                          if(p == "" ){
                              p = this.properties.xname;
                              alert("Validation Error");
                          }
                      }
                  }
                   
                  w.show();
                  

                   

                   

                   

                  --

                  Marijan (tomaxxi)

                  • 6. Re: [CS4/CS5] ScriptUI focus event (Win/Mac)
                    Marc Autret Level 4

                    Thanks a lot, Marijan.

                     

                    Your solution is pretty similar to the 'flag approach' I temporarily implemented. Here is the logics:

                     

                    var     u,
                         stopAlert = false, // our FLAG
                         w = new Window('dialog',u,u,{xname:'Window'}),
                         e1 = w.add('edittext', u, "",{xname:'EditText (1)'}),
                         e2 = w.add('edittext', u, "",{xname:'EditText (2)'}),
                         eInfo = w.add('edittext', [0,0,300,400], "",{xname: 'EditText (Info)', multiline:true}),
                         b = w.add('button',u,'Clear', {xname:'Button'});
                     
                    e1.characters = e2.characters = 20;
                     
                    e1.onDeactivate = e2.onDeactivate = function()
                    {
                         if( isNaN(this.text) )
                              {
                              this.active = true;
                              eInfo.text += "--- Validation Error\n";
                              if( !stopAlert )
                                   {
                                   stopAlert = true;
                                   alert("Validation Error");
                                   }
                              }
                    };
                     
                    e1.onChanging = e2.onChanging = function()
                    {
                         stopAlert = false;
                    };
                    
                    
                    w.show();
                    
                    

                     

                    OK but this really sounds like a hack to me, and there are still remaining issues in more complex event hierarchy.

                    No way to actually manage the focus event propagation?

                     

                    @+

                    Marc

                    • 7. Re: [CS4/CS5] ScriptUI focus event (Win/Mac)
                      Marijan Tompa [tomaxxi] Level 4

                      Yes, I agree, it's a hack!

                       

                      Hmm....

                       

                      In my oppinion there is some serious focus BUG inside ScriptUI.

                      When I run this code and switch between fields alert pops up.

                      After I dismiss alert dialog, I can't continue typing even if

                      UI shows that my text field is focused!

                       

                      var     u,
                           w = new Window('dialog',u,u,{xname:'Window'}),
                           e1 = w.add('edittext', u, "",{xname:'EditText (1)'}),
                           e2 = w.add('edittext', u, "",{xname:'EditText (2)'}),
                           eInfo = w.add('edittext', [0,0,300,400], "",{xname: 'EditText (Info)', multiline:true}),
                           b = w.add('button',u,'Clear', {xname:'Button'});
                      
                      e1.characters = e2.characters = 20;
                      
                      e1.onDeactivate = e2.onDeactivate = function()
                      {
                           alert("Validation Error");
                      };
                      
                      w.show();
                      

                       

                      --

                      Marijan (tomaxxi)

                      • 8. Re: [CS4/CS5] ScriptUI focus event (Win/Mac)
                        Marc Autret Level 4

                        Just a question, Marijan: did you test your samples in Mac or Windows?

                         

                        Thanks,

                        Marc

                        • 10. Re: [CS4/CS5] ScriptUI focus event (Win/Mac)
                          Marc Autret Level 4

                          Yep!

                           

                          In Mac the command myEditText.active = true; does not seem to actually change the focus, and causes weird GUI effects.

                          (Looking for another strategy through AppleScript...)

                           

                          @+

                          Marc

                          • 11. Re: [CS4/CS5] ScriptUI focus event (Win/Mac)
                            Marijan Tompa [tomaxxi] Level 4

                            I don't have Mac so I can't help you, but I can wish you luck!

                             

                            --

                            Marijan.

                            • 12. Re: [CS4/CS5] ScriptUI focus event (Win/Mac)
                              Harbs. Level 6

                              It does seem to change the focus, but you are still left with a cursor sometimes (which does not work).

                               

                              The one that has the focus rect allows input and .active = true seems to work fine. (Mac 10.6 CS5)

                               

                              Harbs

                              • 13. Re: [CS4/CS5] ScriptUI focus event (Win/Mac)
                                Marc Autret Level 4

                                Harbs. wrote:

                                 

                                It does seem to change the focus, but you are still left with a cursor sometimes (which does not work).

                                 

                                The one that has the focus rect allows input and .active = true seems to work fine. (Mac 10.6 CS5)

                                 

                                Harbs

                                 

                                Well, this is not so clear to me. My beta-tester —also Mac CS5— reports unpredictable behaviors. It also depends on how the user change the focus: using the TAB key and clicking into another field are not equivalent. (It seems that the change-focus-by-click event is more complicated to counteract...)

                                 

                                But generally, on Mac platforms, we cannot be sure that setting myEditText.active = true gives the focus back to myEditText when other user events are running. The problem is that the active property is not always 'reliable'. We found that (under unstable circumstances) e1.active might returns true while e2 actually owns the focus ring! Somehow, Mac systems can dissociate the 'active' control and the focus... I don't know.

                                 

                                My assumption is that the focus has a high level priority in the Mac event loop and that it closely mirrors the user interaction, while the active property is an abstract thing featured by the ScriptUI layer. The purpose of ScriptUI is to wrap OS events and widgets in a single scripting interface for both Mac and Win, but ScriptUI is nothing but a bridge. I suppose that "myWidget is active" and "myWidget has the focus" means exactly the same thing in Windows, so ScriptUI can easily target the focus through the active property in Win environment. But Mac OS doesn't work the same way.

                                 

                                Interestingly when we set an EditText as borderless in Windows, we entirely remove the default widget appearence. On the contrary, a borderless EditText in Mac still reserve an additional region for the focus and there is no way to hide the focus ring when the control receives the inputs.

                                 

                                In addition, I posted above a screenshot where two EditText instances have the focus ring at the same time. I'm not a Mac user but I was told that this is not a normal situation. Then playing with the active property in ScriptUI can really disrupt the focus behavior on Mac platforms...

                                 

                                My first idea was to study more closely event triggering and to use a timestamp-based routine to keep the control of the focus. So I sent the following script to my beta-tester:

                                 

                                var     u,
                                     w = new Window('dialog',u,u,{xname:'Window'}),
                                     e1 = w.add('edittext', u, "aaa",{xname:'(1)'}),
                                     e2 = w.add('edittext', u, "",{xname:'(2)'}),
                                     eInfo = w.add('edittext', [0,0,300,400], "",{xname: '(Info)', multiline:true}),
                                     cForce = w.add('checkbox', u, "Force default value on error"),
                                     b = w.add('button',u,'Clear', {xname:'(Button)', name:'ok'});
                                 
                                e1.characters = e2.characters = 20;
                                
                                var fgTarget = null,
                                     fgTimeStamp = +new Date,
                                     fgAlerting = null,
                                     fgNonValid;
                                
                                var blurEventHandler = function(ev)
                                     {
                                     if( fgAlerting ) return;
                                
                                     var d = +new Date - fgTimeStamp;
                                     if( 100 < d )
                                          {
                                          eInfo.text = '';
                                          fgTarget = ev.target;
                                          fgTimeStamp = +new Date;
                                          fgAlerting = null;
                                          fgNonValid = isNaN(fgTarget.text);
                                          }
                                     d += ' ms';
                                
                                     eInfo.text += (ev.target.properties.xname + ' is losing the focus  ' + d + '\n');
                                
                                     fgTarget.active = true;
                                     
                                     if( fgNonValid )
                                          {
                                          eInfo.text += ('  Re-activate ' + fgTarget.properties.xname + 
                                               ' from '+ ev.target.properties.xname + '\n' + 
                                               '  w.active=' + w.active + '\n' +
                                               '  e1.active=' + e1.active + '\n' +
                                               '  e2.active=' + e2.active + '\n') ;
                                          if( null===fgAlerting )
                                               {
                                               fgAlerting = true;
                                               eInfo.text += '--- ALERT ---\n';
                                               alert("Please enter a numeric value or let the field empty.");
                                               fgAlerting = false;
                                               if( cForce.value ) fgTarget.text = '50';
                                               fgTimeStamp = +new Date;
                                               }
                                          }
                                     };
                                
                                e1.addEventListener('blur',blurEventHandler);
                                e2.addEventListener('blur',blurEventHandler);
                                
                                var anyEventHandler = function(ev)
                                     {
                                     eInfo.text += (ev.target.properties.xname + ' -> ' + ev.type + '\n');
                                     };
                                
                                e1.addEventListener('mousedown',anyEventHandler);
                                e1.addEventListener('mouseup',anyEventHandler);
                                e1.addEventListener('keydown',anyEventHandler);
                                e1.addEventListener('keyup',anyEventHandler);
                                e2.addEventListener('mousedown',anyEventHandler);
                                e2.addEventListener('mouseup',anyEventHandler);
                                e2.addEventListener('keydown',anyEventHandler);
                                e2.addEventListener('keyup',anyEventHandler);
                                
                                b.onClick = function(){eInfo.text='';}
                                
                                e1.active = true;
                                w.show();
                                

                                 

                                This script gives unstable results. ScriptUI does not always report every event that actually occurs.

                                But as a general rule, what the user has done supersedes what the script can do.

                                 

                                Any help from ScriptUI / Mac gurus would be highly welcome.

                                 

                                @+

                                Marc

                                • 14. Re: [CS4/CS5] ScriptUI focus event (Win/Mac)
                                  Harbs. Level 6

                                  Yes, There is a difference between tabbing between elements and clicking, but it appears to just be a visual artifact.

                                   

                                  When tabbing between them, the cursor appears in both text edit boxes using your code, but clicking on a different box causes there to be a single cursor in the new box. In both cases, the focus rect appears around the correct box, and only the box with the focus rect accepts input.

                                   

                                  I'm not sure how you got the two focus rects that appear in your screen shot...

                                   

                                  I'll try to get some feedback on this from the Script UI engineers...

                                   

                                  HTH,

                                  Harbs

                                  • 15. Re: [CS4/CS5] ScriptUI focus event (Win/Mac)
                                    Marc Autret Level 4

                                    I'm not sure how you got the two focus rects that appear in your screen shot...

                                     

                                    Honestly I'm not sure either! But we got it by playing with the active property.

                                     

                                    I'll try to get some feedback on this from the Script UI engineers...

                                     

                                    It's very nice of you. I hope we have lights.

                                     

                                    @+

                                    Marc

                                    • 16. Re: [CS4/CS5] ScriptUI focus event (Win/Mac)
                                      Peter Kahrel Adobe Community Professional & MVP

                                      Hi Marc,

                                       

                                      Here's an example that Mark Francis put together for me a four years ago when I had the same problem (refocusing an edittext control). It's another way around the limitation of [edittext].active.

                                       

                                       

                                      var w = new Window ("dialog");
                                         var grp = w.add ("group");
                                            grp.add ("statictext", undefined, "Text:");
                                            var txt = grp.add ("edittext");
                                            txt.characters = 30; txt.active = true;
                                         var buttons = w.add ("group");
                                            buttons.add ("button", undefined, "OK");
                                            buttons.add ("button", undefined, "Cancel");
                                      
                                      /*    Define on onClose event handler to prevent the dialog from
                                          being closed while there is still an error in an input field */
                                      w.onClose = onCloseDialog;
                                      
                                      /*    Define an event listener on the dialog for 'focus' events in any control,
                                          so we can catch attempts to 'focus away' from controls with errors. */
                                      w.addEventListener ('focus', focusEventHandler, true);
                                      
                                      txt.onChange = folderdialog;
                                      
                                      w.show();
                                      
                                      function folderdialog()
                                      {
                                          this.error = (! Folder(this.text).exists);
                                          if (this.error)
                                              {
                                              this.text += ' does not exist';
                                              this.active = true;
                                              }
                                      }
                                      
                                      //    Event handler for dialog 'close' events
                                      function onCloseDialog ()
                                      {
                                          /* In general, code any arbitrary set of error checks here... */    
                                          var okToClose = (! txt.error) /* && other error checks */;
                                          return okToClose;
                                      }
                                      
                                      //    Event handler for 'focus' events
                                      function focusEventHandler (event)
                                      {
                                          if (txt.error)
                                              //    Error: set focus back to the 'getFolder' field
                                              txt.active = true;
                                      }

                                       

                                      Peter