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 |
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. 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? |
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 |
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 |
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 |
On 1 May 2007, at 1:07, Andreas Raab wrote:
Aha! This was an insight that I was missing. Someone should write a book that explains all this stuff ;-)
circle on: #mouseUp send: #value to:[rect addMorph: circle]. which appeared to have no effect.
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 |
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 |
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 |
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 > > |
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 ------------------------------------------------------------------ |
Free forum by Nabble | Edit this page |