Frameworks, Morphic, the World, its Hand, Morphs, and event handling

Raab, Andreas Andreas.Raab at disney.com
Wed Oct 25 07:38:03 UTC 2000


Ned, Lex, Josh, and all,

I've been following the discussion with great interest because I've been
doing most of the recent event/hand rework. The issues you're raising are
all valid to an extent but the much more general question which is raised is
that about framework design and use.

If you look at it, then we basically use frameworks (in particular Morphic)
in two quite different ways: We either "use" it in the form of compositing
and parametrizing certain objects or we "use" by subclassing to modify or
extend certain behavior. 

Use by "subclassing" should normally only be done for changing the framework
itself, since it creates dependencies between objects (superclass <->
subclass) which need to be tracked very carefully. Whenever you use a
framework by subclassing you potentially modify the framework itself which
is a dangerous thing to do. But if you look at Morphic then you'll see that
we almost always use Morphic in this wrong way (my system has 350 subclasses
of Morph) and many of those morphs reimplement crucial framework methods in
a completely broken way.

Use by "composition and parametrization" requires a lot of flexibility from
the outside. That is what (I think) you are referring to with something like
the "event handling policy". The task of the framework is to provide
powerful enough hooks for the user to change the behavior of objects within
the limits of the framework itself.

And here we are at the heart of the problem (in particular in Morphic). Have
a look at the event handler in class Morph. The event handler is one of
those hooks that allow you to modify the reaction to certain events. So that
(for example) you could install an event handler in your construction
toolkit that checks if 'someEvent hand == myHand' (makes it modal to the
hand) and then either performs the action associated with gesture/hand or
passes it on to the former event handler (if any). The reason this doesn't
work is simply that basically *NO* implementor of e.g., #mouseDown: calls
'super mouseDown:' and therefore breaks the framework [I recently checked
the implementors of 'step' which performs a similar critical function in the
framework and out of the 90 implementors only 20 sent anything to super].

Given that our framework wouldn't be broken in so many places you could do
exactly what you want from within the framework with a reasonable bit of
work. However, because our framework *is* broken in so many places it often
seems to be easier to bypass all those places that are broken by having some
"external global strategy" which doesn't even bother to call the appropriate
methods. But this is even more dangerous because it leads to the pollution
that we find in HandMorph with #handlesMouseDown: #preemptsMouseDown: and
#trumpsMouseDown:. I find it very likely that any global policy will run
into situations where it needs to do things a little differently and if the
methods that are used by one global policy don't match exactly the
intentions of another global policy you'll start to introduce more hacks to
get around this. Then somebody will see that the two existing global
strategies don't quite do what she wants to do and since both are already so
ugly you better define a new one. And so on...

Of course, even if the framework would be okay, there is a question of
whether something like your hand-based policy for event handling should be
doable or not. One might say "yes of course" but what is happening here is
that you go to the very limits of what might be considered to be "within"
the limits of the framework. Consider, for instance, the following
situation: If we would have an event handling policy that simply does move
morphs from A to B no matter what this morph would usually do with events,
how would you ever get out of this mode?! Trying to press some button would
move this button; trying to get a menu would move the object (world) you're
pressing on, and even if you'd be able to discriminate the world, you would
still move the menu when you'd try to click on it. That leaves you with
something like

MoveEventHandlingPolicy>>handleMouseDown: evt
	target isWorldMorph or:[target isKindOf: MenuMorph or:[target
isKindOf: MenuItemMorph ...] ifTrue:[^target mouseDown: evt].
	"start dragging"

And of course, whenever the framework changes you'll have to adjust your
tests accordingly. The only alternative is to integrate the above
modifications into the framework directly, e.g., to "change" the classes in
question. And it's the right thing to do for the above case since you are in
fact establishing completely different semantics for the framework itself.
Changing framework semantics is different from using the framework and if
you really want to change the semantics the right place to do it is from
inside the framework and not from the outside.

In the end, I think that having any "outside" strategy for
framework-critical behavior is a Very Bad Idea. HandMorph's event handling
rules have clearly shown that. In the long term, it will always become
incomprehensible, static, and slow. However, I agree that you should be able
to do what you are trying to do (e.g., use different semantics for the
gestures the framework recognizes) by parametrization. The reason this
currently doesn't work is simply that too many subclasses of Morph break the
framework and also that the semantics of certain operations is not
well-defined (like: when exactly are the eventHandlers called and what is
the relation to any other #mouseDown: implementation) and incomplete (e.g.,
no "drop handler" and similar).

Do I need to mention what I'm working at these days?! :-)

  - Andreas





More information about the Squeak-dev mailing list