1

Is it possible to do something like this

while (view.mouseover == true) {
    preform action
}

I want to have an action repeat for as long as the mouse is over a specific view.
(asked on the laszlo-user mailing list)

raju-bitter
  • 8,906
  • 4
  • 42
  • 53

2 Answers2

1

Since both ActionScript and JavaScript are single threaded, it's not possible to have a while loop with pauses between each loop iteration. In the SWF10/11 runtime, you need to make sure that the code within each method or function can be executed within one frame (duration depends on the framerate of the SWF clip) of your application.

As a workaround you can use a timer, here is an example:

<canvas debug="true">

    <class name="mouseoverview" extends="view">        <attribute name="timer" type="object" value="null" />
        <!-- lz.Delegate instance used by the timer -->
        <attribute name="timerdel" type="object" value="null" />
        <attribute name="timeractive" type="boolean" value="false" />
        <!-- milliseconds to pause before each call to doWhileMouseover method -->
        <attribute name="tick" type="number" value="500" />
        <handler name="onmouseover">
            Debug.info('mouseover');
            if (this.timeractive == false) {
                this.setAttribute('timeractive', true);
                this.timerdel = new lz.Delegate( this, "timerCallback" );
                this.timer = lz.Timer.addTimer( this.timerdel, this.tick );
                // When the timer is activated, do one call to the method
                // containing the loop logic. The following calls will be
                // handled by the timer and delegate.
                this.doWhileMouseover();
            }
        </handler>
        <handler name="onmouseout">
            Debug.info('mouseout');
            if (this.timeractive) {
                this.setAttribute('timeractive', false);
                lz.Timer.removeTimer(this.timerdel);
            }
        </handler>
        <method name="timerCallback" args="millis">
            if (this.timeractive) {
                lz.Timer.resetTimer(this.timerdel, this.tick);
                this.doWhileMouseover();
            }
        </method>
        <method name="doWhileMouseover">
            Debug.info("This is your virtual while loop for mouseover");
        </method>
    </class>

    <mouseoverview x="100"
          y="100"
          width="400"
          height="400"
          bgcolor="#33aaff"
          tick="250">

    </mouseoverview>

</canvas>

When a mouseover occurs, a timer is started using the timerdel (an instance of lz.Delegate). Then the doWhileMouseover() method is called once directly, and then repeatedly using the timer, as long as no onmouseout event happened.

raju-bitter
  • 8,906
  • 4
  • 42
  • 53
1

Well, it looks like you answered your own question while I was testing my solution to make sure it worked correctly, but here is an alternative solution that works under OpenLaszlo 4.9.0 SWF10 and OpenLaszlo 4.9.0 DHTML run-times:

<canvas width="1000" height="665" debug="true">

  <view id="v" bgcolor="0xcccccc" width="200" height="200">

    <!--- @param boolean mouseisover: true when the mouse is over -->
    <attribute name="mouseisover" type="boolean" value="false" />

    <!--- @keywords private -->
    <!--- @param lz.Delegate dlgt_repeat: stores the lz.Delegate object --> 
    <attribute name="dlgt_repeat" type="expression" />

    <!--
    Called when the 'onmouseover' event occurs
    -->
    <handler name="onmouseover">

      // Step 1) unregister any existing delegate 
      // mark it for garbage collection 
      // and prevent its event from triggering:

      if (this['dlgt_repeat'])
        this.dlgt_repeat.unregisterAll(); 

      // Step 2) update this.mouseisover flag:

      if (!this.mouseisover)
        this.setAttribute('mouseisover', true);

      // Step 3) create an event Delegate and call it 
      // on the next application idle event:

      var objDlgt = new lz.Delegate(this, 'doSomething');       
      this.setAttribute('dlgt_repeat', objDlgt);

      lz.Idle.callOnIdle(this.dlgt_repeat);

    </handler>

    <!--
    Called when the 'onmouseout' event occurs
    --> 
    <handler name="onmouseout">

      // Step 1) unregister any existing delegate 
      // mark it for garbage collection 
      // and prevent its event from triggering: 

      if (this['dlgt_repeat'])
        this.dlgt_repeat.unregisterAll();   

      // Step 2) Update this.mouseisover flag:

      if (this.mouseisover)
        this.setAttribute('mouseisover', false);    

    </handler>

    <!--- @keywords private -->
    <!--- 
    Called on application idle event by lz.Idle repeatedly 
    when the mouse is down.

    @param ??? objDummy: required for SWF9+ run-times for methods 
    called by delegates due to AS3 (ActionScript3 compiler 
    requirements). Just set default to null to make compiler 
    happy and ignore...
    -->
    <method name="doSomething" args="objDummy=null">
    <![CDATA[

      // Note: CDATA allows '&&' to be used in script below, 
      // alternatively omit CDATA and use '&amp;&amp;' instead
      // of '&&'

      // Step 1) Execute your code you want to run here:  

      if ($debug) Debug.debug('Do something...');

      // Step 2): If mouse is still over and the event 
      // delegate exists then schedule the event to be 
      // executed upon the next application idle state:

     if (this.mouseisover && this['dlgt_repeat'] != null)
       lz.Idle.callOnIdle(this.dlgt_repeat);

   ]]>
   </method>

    <text text="Move mouse over" />

  </view>

</canvas>
Kmeixner
  • 1,664
  • 4
  • 22
  • 32
  • From an API perspective, I prefer your approach. But the idle event in the DHTML runtime just uses a setInterval() call based on the "framerate" for the DHTML application. Of course, there's no such thing as a framerate in DHTML, but OpenLaszlo tries to emulate the Flash APIs in this case. There is still an open issue regarding the idle event handling in the DHTML kernel [LPP-8866](http://jira.openlaszlo.org/jira/browse/LPP-8866). Therefore I feel more comfortable using a timer in such situations, unless I only plan to use the SWFx runtime. – raju-bitter Sep 18 '12 at 20:43
  • Only to make this clear: if the focus is on making calls to the loop as often as possible in the SWFx runtime, the lz.Idle.callOnIdle() approach should be preferred, right? –  Sep 18 '12 at 21:00
  • That's correct. Unfortunately there is no browser JavaScript API available for retrieving CPU load information, although the W3C has been working on a specification since 2010: The [System Information API](http://www.w3.org/TR/2010/WD-system-info-api-20100202/#cpu) – raju-bitter Sep 18 '12 at 21:12
  • @Kmeixner I've accepted your answer, since it is the recommended way based on the OpenLaszlo documentation. – raju-bitter Sep 18 '12 at 22:36
  • Thanks, I just chose the idle event under the assumption it would execute faster than a timer event, but using a timer should have a similar effect. In my own application I use a more accurate timer than lz.Timer even, I shared it on the OpenLaszlo forums once: http://forum.openlaszlo.org/showthread.php?t=13219 – Kmeixner Sep 18 '12 at 23:23