Another stupid Morphic Question

Previous Topic Next Topic
 
classic Classic list List threaded Threaded
10 messages Options
Reply | Threaded
Open this post in threaded view
|

Another stupid Morphic Question

Prof. Andrew P. Black
In my continuing quest to make sense of Morphic, I've run up against the following situation.

I have a RectangleMorph (subclass).  Embedded in it are a number of CircleMorph (again, actually a subclass) connected by NCAAConnectorMorphs.

I want to be able to move the circles by dragging them with the red button.  This is exactly the default behavior when they are in the World — but when they are in the RectagleMorph, a mouse click picks up the whole rectangle.  I have tried making handleMouseDown: answer false in the RectangleMorph, and to answer true in the circles — this doesn't help. 

Conversely, I _don't_ want to be able to pick up the Rectangle with a single red click.  But I can, and I can't turn this off, even though handlesMouseDown: and handlesMouseStillDown: answer false.

This should be trivial, shouldn't it?

Andrew

________________________________________________
The Morphic tarpit: where everything is possible ... but nothing is easy


Reply | Threaded
Open this post in threaded view
|

Re: Another stupid Morphic Question

Brad Fuller-3
Andrew P. Black wrote:

> In my continuing quest to make sense of Morphic, I've run up against
> the following situation.
>
> I have a RectangleMorph (subclass).  Embedded in it are a number of
> CircleMorph (again, actually a subclass) connected by NCAAConnectorMorphs.
>
> I want to be able to move the circles by dragging them with the red
> button.  This is exactly the default behavior when they are in the
> World — but when they are in the RectagleMorph, a mouse click picks up
> the whole rectangle.  I have tried making handleMouseDown: answer
> false in the RectangleMorph, and to answer true in the circles —
> this doesn't help.
Don't know what you mean: you can click again to receive the halo's of a
morph that is in another morph.  Just keep clicking until you get the
morph you want (you have to point to it). Is that what you want?

Reply | Threaded
Open this post in threaded view
|

Re: Another stupid Morphic Question

Tom Phoenix
In reply to this post by Prof. Andrew P. Black
On 4/30/07, Andrew P. Black <[hidden email]> wrote:

> I have a RectangleMorph (subclass).  Embedded in it are a number of
> CircleMorph (again, actually a subclass) connected by NCAAConnectorMorphs.
>
> I want to be able to move the circles by dragging them with the red button.
> This is exactly the default behavior when they are in the World — but when
> they are in the RectagleMorph, a mouse click picks up the whole rectangle.
> I have tried making handleMouseDown: answer false in the RectangleMorph, and
> to answer true in the circles — this doesn't help.
>
> Conversely, I _don't_ want to be able to pick up the Rectangle with a single
> red click.  But I can, and I can't turn this off, even though
> handlesMouseDown: and handlesMouseStillDown: answer false.

I'm not an expert, so what follows is from my own experience trying to
do something similar.

If handlesMouseDown: answers false, that means that your object
doesn't handle mouse down events, so somebody else has to decide how
to handle them. I think you want the responsive object (the circle) to
answer true to handlesMouseDown:, and you want a handleMouseDown:
method that marks the click as handled if you don't want somebody else
to handle it. My object (which responds differently than yours) used
this handler:

    handleMouseDown: evt

        evt controlKeyPressed ifTrue: [^super handleMouseDown: evt].

        evt redButtonPressed ifTrue: [ "pick it up"
            evt wasHandled: true.
            self removeHalo.
            ^evt hand grabMorph: self from: owner].

        evt yellowButtonPressed ifTrue: [ "control menu"
            evt wasHandled: true.
            self removeHalo.
            ^self invokeMetaMenu: evt ].

        ^super handleMouseDown: evt

Good luck with it!

--Tom Phoenix

Reply | Threaded
Open this post in threaded view
|

Re: Another stupid Morphic Question

Prof. Andrew P. Black
Thanks for your help Tom.  I am making progress, but I'm not there yet.

Brad: I'm not talking about the Halo operations, which work fine.  What I want to do is make the circles behave on redButtonPressed just as if I were using the "move" halo button.  This is the default behavior when the circle is in the World.

The key phrase was: "If handlesMouseDown: answers false, that means that your object
doesn't handle mouse down events, _so somebody else has to decide how
to handle them_".  So by setting handlesMouseDown to false, I was making the morph react to mouse down, which is a bit counter-intuitive.  The way to make it _not_ respond to mouse down is to have handlesMouseDown: answer true, but to have a mouseDown: evt method that does nothing.  Or maybe it should do 
evt wasHandled: true.

OK, so the converse is true.  if I want my circles to handle Mouse Down, then they need to answer true to handlesMouseDown: (that's easy), and then there needs to be a mouseDown: evt method that does the "right thing".  However, the right thing is very complicated!  I tried the following, based on Tom's code:


mouseDown: evt
evt redButtonPressed
ifTrue: ["pick it up"
evt wasHandled: true.
self removeHalo.
^ evt hand grabMorph: self from: owner]

but this glues the morph to the hand, and I have to click again (rather than releasing the red button) to drop it.  Once dropped, the circle is no longer embedded in its (former) owner.

I could probably find all of the bits of code that I need, to handle mouse move and so on, taking care of the offset between mouse click event and the origin of the Morph that I'm moving — most of the code must be in HaloMorph.  But this was the Default Behavior of the circle before I embedded it in the rectangle — surely there must be an easier way to get that default behavior back, other than duplicating the code from whereever it is hidden!

Andrew

Andrew P. Black
Department of Computer Science
Portland State University
+1 503 725 2411





Reply | Threaded
Open this post in threaded view
|

Re: Another stupid Morphic Question

Andreas.Raab
Andrew P. Black wrote:
> The key phrase was: "If handlesMouseDown: answers false, that means that
> your object
> doesn't handle mouse down events, _so somebody else has to decide how
> to handle them_".  So by setting handlesMouseDown to false, I was making
> the morph react to mouse down, which is a bit counter-intuitive.  The
> way to make it _not_ respond to mouse down is to have handlesMouseDown:
> answer true, but to have a mouseDown: evt method that does nothing.

That is correct. It's a bit counter-intuitive because it's the world
which implements the default behavior of dragging objects. In other
words the action is contextual (objects in the world can be dragged) not
builtin.

> OK, so the converse is true.  if I want my circles to handle Mouse Down,
> then they need to answer true to handlesMouseDown: (that's easy), and
> then there needs to be a mouseDown: evt method that does the "right
> thing".  However, the right thing is very complicated!  I tried the
> following, based on Tom's code:

This code seems both overly complicated as well as at least somewhat
buggy (grabMorph:from: should only be used for owner-less morphs). Try
the following instead:

rect := RectangleMorph new.
rect extent: 100@100.
circle := EllipseMorph new.
circle extent: 100@100.
rect addMorphCentered: circle.
rect on: #mouseDown send: #value to:["ignore drags"].
circle on: #mouseDown send: #value
        to:[circle world primaryHand grabMorph: circle].
rect openInWorld.


> I could probably find all of the bits of code that I need, to handle
> mouse move and so on, taking care of the offset between mouse click
> event and the origin of the Morph that I'm moving — most of the code
> must be in HaloMorph.  But this was the Default Behavior of the circle
> before I embedded it in the rectangle — surely there must be an easier
> way to get that default behavior back, other than duplicating the code
> from whereever it is hidden!

Well, by far the easiest way is to use a PasteUpMorph instead of a
RectangleMorph - PasteUps have this behavior builtin.

Cheers,
   - Andreas

Reply | Threaded
Open this post in threaded view
|

Re: Another stupid Morphic Question

Prof. Andrew P. Black

On 1 May 2007, at 1:07, Andreas Raab wrote:

... it's the world which implements the default behavior of dragging objects. In other words the action is contextual (objects in the world can be dragged) not builtin.

Aha!  This was an insight that I was missing.  Someone should write a book that explains all this stuff ;-)

This code seems both overly complicated as well as at least somewhat buggy (grabMorph:from: should only be used for owner-less morphs). Try the following instead:


rect := RectangleMorph new.

rect extent: 100@100.

circle := EllipseMorph new.

circle extent: 100@100.

rect addMorphCentered: circle.

rect on: #mouseDown send: #value to:["ignore drags"].

circle on: #mouseDown send: #value

       to:[circle world primaryHand grabMorph: circle].

rect openInWorld.


Well, this is much more elegant: the use of on:send:to: simplifies things considerably, and, along the way, explains how to use EventHandlers, which were another mystery.  But it has the same bug: once the circle has been "picked up", it is no longer a submorph of the rectangle.  Presumably that could be fixed by a #mouseUp handler, although I tried adding

circle on: #mouseUp   send: #value
       to:[rect addMorph: circle].

which appeared to have no effect.


I could probably find all of the bits of code that I need, to handle mouse move and so on, taking care of the offset between mouse click event and the origin of the Morph that I'm moving — most of the code must be in HaloMorph.  But this was the Default Behavior of the circle before I embedded it in the rectangle — surely there must be an easier way to get that default behavior back, other than duplicating the code from whereever it is hidden!


Well, by far the easiest way is to use a PasteUpMorph instead of a RectangleMorph - PasteUps have this behavior builtin.


That is the answer I was looking for!  Inter alia, it explains what a PasteUpMorph is for, somthing that I had never appreciated (except to know that the World was one).  


Thank you!


Andrew P. Black
Department of Computer Science
Portland State University
+1 503 725 2411





Reply | Threaded
Open this post in threaded view
|

Re: Another stupid Morphic Question

Mikael Kindborg-2
In reply to this post by Andreas.Raab
2007/5/1, Andreas Raab <[hidden email]>:
>
> rect on: #mouseDown send: #value to:["ignore drags"].
> circle on: #mouseDown send: #value
>         to:[circle world primaryHand grabMorph: circle].
> rect openInWorld.
>
Many thanks for this example! I have never thought of sending #value
to a block in an event handler. Nice!
Micke

Reply | Threaded
Open this post in threaded view
|

Re: Another stupid Morphic Question

Andreas.Raab
In reply to this post by Prof. Andrew P. Black
Andrew P. Black wrote:

> Well, this is much more elegant: the use of on:send:to: simplifies
> things considerably, and, along the way, explains how to
> use EventHandlers, which were another mystery.  But it has the same bug:
> once the circle has been "picked up", it is no longer a submorph of the
> rectangle.  Presumably that could be fixed by a #mouseUp handler,
> although I tried adding
>
> circle on: #mouseUp   send: #value
>        to:[rect addMorph: circle].
>
> which appeared to have no effect.

Yes, indeed, it would have no effect. Once you asked the hand to grab
the morph the hand is in control and not the morph. I think I
misunderstood that part - if you simply want to move the circle inside
the rectangle (without taking it out of the structure) you should be
doing something like this:

rect := RectangleMorph new.
rect extent: 100@100.
circle := EllipseMorph new.
circle extent: 100@100.
rect addMorphCentered: circle.
rect on: #mouseDown send: #value to:["ignore drags"].

circle on: #mouseDown send: #value:
   to:[:evt| offset := circle position - evt hand position.
             circle addDropShadow].

circle on: #mouseMove send: #value:
   to:[:evt| circle position: evt hand position + offset].

circle on: #mouseUp send: #value
   to:[circle removeDropShadow].

rect openInWorld.

>> Well, by far the easiest way is to use a PasteUpMorph instead of a
>> RectangleMorph - PasteUps have this behavior builtin.
>>
>
> That is the answer I was looking for!  Inter alia, it explains what a
> PasteUpMorph is for, somthing that I had never appreciated (except to
> know that the World was one).

Well, partly. PasteUps provide the default drag and drop you see in the
world but they *do* rip morphs out of their structure when you click on
them. If your entities are sensitive to structural changes (or if they
need to preserve the z-order) you'll be better off with something like
the above.

Cheers,
   - Andreas

Reply | Threaded
Open this post in threaded view
|

Re: Another stupid Morphic Question

Andreas.Raab
PS. Forgot to mention that of course you can contextualize those actions
just as well. For example try the following (which has all the actions
associated with the container):

rect := RectangleMorph new.
rect extent: 200@200.

#(red green blue yellow) do:[:cc|
   circle := EllipseMorph new.
   circle extent: 50@50.
   circle color: (Color perform: cc).
   circle position: (0 to: 150) atRandom @ (0 to: 150) atRandom.
   rect addMorph: circle.
].

rect on: #mouseDown send: #value: to:[:evt|
   list := rect rootMorphsAt: evt position.
   target := list isEmpty ifTrue:[nil] ifFalse:[list first].
   target ifNotNil:[
     target comeToFront.
     offset := target position - evt hand position.
     target addDropShadow]].

rect on: #mouseMove send: #value: to:[:evt|
   target ifNotNil:[target position: evt hand position + offset]].

rect on: #mouseUp send: #value to:[
   target ifNotNil:[
     target removeDropShadow.
     target := nil]].

rect openInWorld.

Cheers,
   - Andreas

Andreas Raab wrote:

> Andrew P. Black wrote:
>> Well, this is much more elegant: the use of on:send:to: simplifies
>> things considerably, and, along the way, explains how to use
>> EventHandlers, which were another mystery.  But it has the same bug:
>> once the circle has been "picked up", it is no longer a submorph of
>> the rectangle.  Presumably that could be fixed by a #mouseUp handler,
>> although I tried adding
>>
>> circle on: #mouseUp   send: #value
>>        to:[rect addMorph: circle].
>>
>> which appeared to have no effect.
>
> Yes, indeed, it would have no effect. Once you asked the hand to grab
> the morph the hand is in control and not the morph. I think I
> misunderstood that part - if you simply want to move the circle inside
> the rectangle (without taking it out of the structure) you should be
> doing something like this:
>
> rect := RectangleMorph new.
> rect extent: 100@100.
> circle := EllipseMorph new.
> circle extent: 100@100.
> rect addMorphCentered: circle.
> rect on: #mouseDown send: #value to:["ignore drags"].
>
> circle on: #mouseDown send: #value:
>   to:[:evt| offset := circle position - evt hand position.
>             circle addDropShadow].
>
> circle on: #mouseMove send: #value:
>   to:[:evt| circle position: evt hand position + offset].
>
> circle on: #mouseUp send: #value
>   to:[circle removeDropShadow].
>
> rect openInWorld.
>
>>> Well, by far the easiest way is to use a PasteUpMorph instead of a
>>> RectangleMorph - PasteUps have this behavior builtin.
>>>
>>
>> That is the answer I was looking for!  Inter alia, it explains what a
>> PasteUpMorph is for, somthing that I had never appreciated (except to
>> know that the World was one).
>
> Well, partly. PasteUps provide the default drag and drop you see in the
> world but they *do* rip morphs out of their structure when you click on
> them. If your entities are sensitive to structural changes (or if they
> need to preserve the z-order) you'll be better off with something like
> the above.
>
> Cheers,
>   - Andreas
>
>


Reply | Threaded
Open this post in threaded view
|

Re: Another stupid Morphic Question

Noury Bouraqadi
In reply to this post by Prof. Andrew P. Black
Andrew,

Did you considered using PasteUpMorph instead of ReactangleMorph. No  
code will then be necessary.
BTW, in the parts bin, instances of PasteUpMorph are named "Playfield".

Noury
Le 30 avr. 07 à 19:29, Andrew P. Black a écrit :

> In my continuing quest to make sense of Morphic, I've run up  
> against the following situation.
>
> I have a RectangleMorph (subclass).  Embedded in it are a number of  
> CircleMorph (again, actually a subclass) connected by  
> NCAAConnectorMorphs.
>
> I want to be able to move the circles by dragging them with the red  
> button.  This is exactly the default behavior when they are in the  
> World — but when they are in the RectagleMorph, a mouse click picks  
> up the whole rectangle.  I have tried making handleMouseDown:  
> answer false in the RectangleMorph, and to answer true in the  
> circles — this doesn't help.
>
> Conversely, I _don't_ want to be able to pick up the Rectangle with  
> a single red click.  But I can, and I can't turn this off, even  
> though handlesMouseDown: and handlesMouseStillDown: answer false.
>
> This should be trivial, shouldn't it?
>
> Andrew
>
> ________________________________________________
> The Morphic tarpit: where everything is possible ... but nothing is  
> easy
>

Noury
------------------------------------------------------------------
Dr. Noury Bouraqadi - Enseignant/Chercheur
Responsable de l'option I.S.I.C.
ARMINES - Ecole des Mines de Douai - Dept. I.A.
http://csl.ensm-douai.fr/noury

European Smalltalk Users Group Board
http://www.esug.org
------------------------------------------------------------------