Futurescale, Inc. PureMVC Home

The PureMVC Framework Code at the Speed of Thought


Over 10 years of community discussion and knowledge are maintained here as a read-only archive.

New discussions should be taken up in issues on the appropriate projects at https://github.com/PureMVC

Pages: [1]
Print
Author Topic: Vslider changeEvent & Zoom?  (Read 14932 times)
jgervin
Courseware Beta
Sr. Member
***
Posts: 50


View Profile Email
« on: March 08, 2009, 01:33:30 »

I was wondering if someone can lend me a hand brain on this issue.  I have an application that has a Vslider. When you navigate the vslider a sprite on the stage should scale depending if the user is increasing/decreasing the slider.  Nothing to abnormal here....user can click on the slider, drag the slider, all the normal stuff (livedragging = true). I listen to the change event of the slider when that event is dispatched I send a notification that my sprite should change its scale based on the data that was sent with the notification.

The catch is that with a keyboard(ctrl) + mouse drag the user can scale the sprite independent of the Vslider.  The issue is that when you do the ctrl+mouse drag, I have to tell the Vslider that the sprites scale has changed so it, the vslider, needs to change its value to match, so say we scaled the sprite to 1.25, I need to tell the vslider that it shouldn't be scale=1 anymore and it needs to match the sprites scale.

The issue is that when I tell the slider to move it dispatches its changed event, which then causes it to dispatch its changeevent but the sprite is s already the correct size.  The other issue is that the vslider I can set via sending it a zoom value, such as in this case 1.25.  But the framework I am using with the sprite doesn't accept values such as 1.25.  It only accepts the difference between 1.25 and 1.00 so .25; 

So my issue is how can I get both to stay in sink?  I think the changeevent of the vslider is only suppose to send the change event when altered by the mouse, but I am doing it via a method, so guessing their is  bug in the code.  Not sure the best way to implement with Puremvc, with cairngorm I would use a global variable or VO and bind to that. 

TIA
Logged
Ondina
Sr. Member
****
Posts: 86


View Profile Email
« Reply #1 on: March 09, 2009, 03:30:50 »

I had a similar problem with synchronization of sliders  and component's input affecting each other's values.

I tried to apply my solution to a sprite to simulate your problem. I used one more slider instead of dragging  the sprite, that changes the sprite's width too.


The idea is that the sliders should dispatch an event on “click” or “drag”  not on “change”.
The scaling factor is for sure not correct in my example and also the synchronization with the max value isn't correct.

It might be kind of a too simple solution, but maybe you can use it though.
Perhaps it could be solved better with StateMachine.

:
<mx:UIComponent id="uic" x="10" y="10" width="400" height="400"/>
<mx:HSlider  id="slider1" snapInterval="1" maximum="60" tickInterval="5"   thumbDrag="clickedSlider1=true;" click="clickedSlider1=true;" labels="[0,10,20,30,40,50,60]"  thumbRelease="scaleSprite(event)" change="scaleSprite(event)"/>
<mx:HSlider  id="slider2" snapInterval="2.5" maximum="70.5" tickInterval="6.5"   thumbDrag="clickedSlider2=true;" click="clickedSlider2=true;" labels="[0,10,20,30,40,50,60]" thumbRelease="scaleSprite(event)" change="scaleSprite(event)"/>
------

private var sprite:Sprite = new Sprite();

private var syncSlider1:Number=0;
private var syncSlider2:Number=0;

private var clickedSlider1:Boolean = false;
private var clickedSlider2:Boolean = false;

private var factor:Number = 1.5;

private function createSprite():void{
    sprite.x = 0;
sprite.y = 0;
sprite.graphics.lineStyle(1,0x000000,1);
sprite.graphics.beginFill(0xff0000,1);
sprite.graphics.drawRect(0,0,100,100);
sprite.graphics.endFill();
uic.addChild(sprite);
}
private function scaleSprite(event:SliderEvent):void{
var currentSlider:Slider = Slider( event.currentTarget );
if(currentSlider.value==0){
resetSlider();
}else{
    if(event.currentTarget.id=="slider1" && clickedSlider1==true){
        clickedSlider1 = false;
        slider2.value = slider1.value+factor;
    }else if (event.currentTarget.id=="slider2" && clickedSlider2==true){
        clickedSlider2 = false;
        slider1.value = slider2.value-factor;
    }
    if(currentSlider.value>syncSlider2){
        sprite.width = sprite.width+currentSlider.value;
        }else{
        sprite.width = sprite.width-currentSlider.value;
        }
    syncSlider2 =  slider2.value;
    syncSlider1 = slider1.value;
  }
  spriteScaled.text = sprite.width.toString();
}
private function resetSlider():void{
sprite.width =100;//original size actually
syncSlider1 = syncSlider2 =  0;
    slider2.value = slider1.value = 0;
}
Logged

~ Ondina ~
jgervin
Courseware Beta
Sr. Member
***
Posts: 50


View Profile Email
« Reply #2 on: March 09, 2009, 06:55:03 »

Thanks for the example.  Unfortunately, I can't use another slider.  Guess I will have a look at State Machine.
Logged
Ondina
Sr. Member
****
Posts: 86


View Profile Email
« Reply #3 on: March 09, 2009, 07:38:57 »

Well, I think it doesn't matter what you use, important thing is to know which of the components is the active one and which is the passive one. 
I see 3 actors: sprite, slider and mouse(+key).
To me the sprite is always passive and the others are active alternatively.

You just need a flag or a state to set the  active component. 
On thumbDrag you set the slider as the active one.
On mouse+key event you set them to the active ones.

The active ones change the values of the passive ones and of themselves and the dimensions of the sprite when they are clicked or dragged or whatever.

If you change the values on changeEvent they are both active or you have an endless loop if you don't know which one has triggered the changes.

If you have the click/drag/mouse/keyboardEvents trigger the changes the actors aren't active simultaneously.
Something like that.

But probably I didn't understand your problem or I'm wrong about my solution.
Logged

~ Ondina ~
jgervin
Courseware Beta
Sr. Member
***
Posts: 50


View Profile Email
« Reply #4 on: March 09, 2009, 08:31:29 »

Sorry you are correct!

You said "You just need a flag or a state to set the  active component.
On thumbDrag you set the slider as the active one.
On mouse+key event you set them to the active ones.

The active ones change the values of the passive ones and of themselves and the dimensions of the sprite when they are clicked or dragged or whatever.

If you change the values on changeEvent they are both active or you have an endless loop if you don't know which one has triggered the changes."

This is exactly what is happening. From what I have read Flex's slider.changeEvent is suppose to be dispatched from mouse manipulation only, but its being dispatched via my programmic changes as well.

Would this be appropriate to use a Proxy and Command?  Maybe the Command can update/retrieve Proxy changes and update which ever component (slider or sprite) did not initiate the change, with the Proxy data?



Logged
Ondina
Sr. Member
****
Posts: 86


View Profile Email
« Reply #5 on: March 09, 2009, 09:29:10 »

Flex Developer's Guide:
"SliderEvent.type property = mx.events.SliderEvent.CHANGE
Dispatched when the slider changes value due to mouse or keyboard interaction"

So it's about the slider's value no matter  who changed it. 
When  you say
slider1.value = 10;
somewhere in a function the thumb moves to this position on the slider and if you have something like:
<mx:HSlider  id="slider1" change="Alert.show(slider1.value.toString())"/>
it will tell you its value(10).
So changing its value  makes it dispatch the changeEvent.

I don't know why you would use a Proxy for that, unless you wanted to change the sprite's dimensions with values coming from a remote source or (config)file or if you wanted to store the sprite's dimensions somewhere. Is this Sprite of yours so important to have a VO for it?

I would make the Mediator of the View containing those sprite and slider responsible for such (UI) things.
A Command? Hmm, as far as I understood a Command would talk only with Mediators and Proxies not with the View(UI).

But I'm still a puremvc-noob, so what I'm saying about Commands&Proxies&Co might not be 100% correct.
Maybe you should tell more about the structure of your application or what do you want to achieve by changing the sprite's dimensions. 
Logged

~ Ondina ~
jgervin
Courseware Beta
Sr. Member
***
Posts: 50


View Profile Email
« Reply #6 on: March 09, 2009, 01:03:47 »

Ok, think google maps.  You can scale/zoom with a slider, but you can also double click the map and it will resize and position, thus updated the slider from the map (map has control).  You can also scale google maps via the mousewheel.  I can do both mousewheel and doubleclick, but I can also do ctrl + move mouse up/down to zoom.  I mean I push ctrl then hold my mouse button down then drag up to zoom in or drag down to zoom out.

So when I try doing the doubleclick, mousewheel, or ctrl-mousedown-drag I play hell trying to get it to work.  I set a flag on vslider component that says if zoom set out side of slider than dirty the flag then when my handleSliderChange function is called it first checks to see if the flag is dirty if so it bails out, returns. It just seems ugly/dirty and breakable.  I had to remove the doubleclick as it was causing all kinds of issues.





Logged
Ondina
Sr. Member
****
Posts: 86


View Profile Email
« Reply #7 on: March 09, 2009, 01:18:40 »

I'll tell you what I think about your map tomorrow. I have to go now. Sorry.
Logged

~ Ondina ~
Ondina
Sr. Member
****
Posts: 86


View Profile Email
« Reply #8 on: March 10, 2009, 02:46:49 »

StateMachine it is!
I think you need it.
I have absolutely no idea how to work with GoogleMaps in general and StateMachine in particular.

I hope someone with a bigger puremvc-brain than mine will help you, but if I had to solve that problem I would start like this:

1.
Define states for the Map
Let's say you have 3 states: MIN MEDIUM MAX ( corresponding to 50x50(px) 100x100 150x150 )- just to keep it simple for now

2.
Then you have the Interactors
Mousewheel (MW)
Doubleclick (DC)
Ctr+MouseDrag (CM)
Slider (SL)

3.
You  need a unit converter for the scale factor for each of the Interactors.
50px  = 1DC =  1MW =  5SL or a scale/zoom factor   ??

4.
In the MapView :
You define Constants for the Events like this:
public static const ON_MOUSEWHEEL_UP:String    = 'onMouseWheelUp';
public static const ON_MOUSEWHEEL_DOWN:String    = 'onMouseWheelDown';
public static const ON_SLIDER_UP:String       = 'onSliderUp';
public static const ON_SLIDER_DOWN:String    = 'onSliderDown';


You can create a custom Event containing the event type, currentTarget, direction(+/-, up/down..), actual position etc for the Mousewheel , DobleClick, Mousedrag, Slider so you can use the information later in your Mediator to identify the Interactor, its actual position...

5.
The MapMediator  will handle the events.

In the MapMediator you added eventListeners for the view events  like this:

mapView.addEventListener( MapView.ON_SLIDER_UP , zoomIn );
mapView.addEventListener( MapView.ON_SLIDER_DOWN , zoomOut );

The methods in MapMediator
zoomIn()
zoomOut()
synchronizeUnits()

In zoomIn() or zoomOut() you change the size of your Map and the state of the map.

Of course you can have only one method scaleMap() , where you zoom in or out depending on the information you got from the event.

The initial state of the map was MAP_MEDIUM.
The action is ACTION_ZOOMIN
The target is MAP_MAX
the next state is MAP_MAXIMIZED (???? )

Here I don't know how to do that, how to properly define the states, when to send a notification, on CHANGE or on EXIT state....
But let's say you send a notification when the Map is already in its MAP_MAXIMIZED  state. When you handle this notification you call the synchronizeUnits() method, which will adjust the values of all the Interactors .

So it  is important that you dispatch the events when the user interacts with the Map ( onDblClick, OnMouseWheel,  onTumbRelease ...) and in the Mediator make the changes to the map's size and then the synchronization of sliders/mouse/clicks and units  when the Maps state changes.

UIInteraction->MapSize->MapState->InteractorState(position, scalefactors...)


Now I don't know if you need a VO or not. Maybe you need one for all the stuff happening to the Map: size, position, additional text.

This is all I could come up with. At least I tried :)

I really hope that someone else could lend you their pure-statemachine-brain on this issue.

Logged

~ Ondina ~
jgervin
Courseware Beta
Sr. Member
***
Posts: 50


View Profile Email
« Reply #9 on: March 10, 2009, 06:57:43 »

Thanks for all your help. I am not good at Statemachine either.  I will try to use it, but hope someone else with Statemachine experience can lend some advice.
Logged
puremvc
Global Moderator
Hero Member
*****
Posts: 2871



View Profile WWW Email
« Reply #10 on: March 10, 2009, 08:56:21 »

If this is purely a view activity (i.e. none of the zoom info needs persistance or modifies the model) I would skip the StateMachine and just create a VO or top level property of the mapping component that has the scale info. The slider should bind both ways to this value. The mouse click and drag alters the vo which causes the slider value to change and the map to zoom. The slider change alters the VO with the same result. This can all be achieved inside the view component with no framework interaction.

Or the slider and mouse drags could bubble to one or more mediators causing notes to fire that result in a mediator setting the VO value. I'd only go this route if there's a good reason to. Encapsulating this sort of behavior in the Flex components is encouraged.

People often erroneously say 'PureMVC doesn't leverage Flex binding, so it can't be the best answer for working with Flex'. This is not the case. Although binding vrom a view component to the model is not encouraged, it is a brilliant way to create robust view components with encapsulated behaviors that can be used elsewhere without need of the framework itself.

-=Cliff>
Logged
jgervin
Courseware Beta
Sr. Member
***
Posts: 50


View Profile Email
« Reply #11 on: March 10, 2009, 10:11:46 »

Thanks Cliff.
Logged
jgervin
Courseware Beta
Sr. Member
***
Posts: 50


View Profile Email
« Reply #12 on: March 23, 2009, 03:25:42 »

Cliff, so how would I bind my views to a model? By model I assume a Proxy with VO object?
Logged
puremvc
Global Moderator
Hero Member
*****
Posts: 2871



View Profile WWW Email
« Reply #13 on: March 23, 2009, 03:41:30 »

Its fine for the view to be aware of the VO classes, just not the Proxies. For portability,always assume (though I certainly hope you won't find the need) that you might decide to ditch PureMVC and go to another framework. If that happened, you'd want to salvage your boundary objects: your data model (VOs) and your view components. The boundary objects should know nothing about the framework.
So, the framework takes care of getting the data frome one boundary to the other. For instance, a Proxy gets a VO back from a service call and sends it in the body of a notification, which an interested Mediator hears. It responds by taking the VO out of the note and setting it on a bindable public property of its view component. Inside the view component, perhaps a form binds to the properties of this object. The VO itself should have bindable properties.

For an example of this see the Flex EmployeeAdmin demo.

-=Cliff>
Logged
jgervin
Courseware Beta
Sr. Member
***
Posts: 50


View Profile Email
« Reply #14 on: March 24, 2009, 08:27:43 »

Cliff, I am beginning to think you have done this before?  ;D
Logged
Pages: [1]
Print