The interaction model in Croquet is being written by Andreas and Dave,
and certainly incorporates the lessons learned from writing and living
with Morphic. Much of the event-handling and scripting code would
work fine in a 2D context as well. I'm inclined to sit back and wait
for the real Croquet release (is there still a summer release target?)
After a sleep, I think that it is not too early to think about
reworking parts of Morphic, but still think that event-handling and
scripting should be left off the list.
So what parts? What is most wrong with Morphic? Do we want to keep
the functionality the same, and focus on cleaning up the code, or do
we want to support new functionality? What new functionality? In
order to make this decision, we need to understand the functionality
that Morphic already has, and how its different parts interact.
Two classes that unquestionably can use some attention are HandMorph
and HaloMorph. This code has grown continually since the introduction
of Morphic, and is difficult to understand and modify. Design
decisions are embedded in the code, and must be understood in context
in order to decide whether to agree with the decision, or to make a
better one now that time has granted us a better perspective. There
have been numerous small tweaks to improve the UI behavior of Morphic;
can we recognize underlying similarites in the motivation for and
implementation of these tweaks (which first need to be identified,
lest they be forgotten in the Morphic reimplementation) in order to
support the same behavior in a cleaner, more extensible fashion?
This is a difficult task. It is made more difficult if backwards
compatability is to be (mostly) maintained, since the natural tendency
is to follow the design decisions that have already been made.
The concerns listed here only scratch the surface. What is the
high-level plan that will allow progress to be made while addressing
such concerns?
Joshua
One thing that's been bouncing around in my head for a while is the
desire for a resolution independent UI based on vector graphics. I was
really interested in Daniel Joyce's post about GTK's use of SVG. I had
been investigating SVG for use in Seaside apps, and was struck by the
thought that it would probably work well as a GUI for Squeak too.
We might want to use SVG only as a serialization format, but the
imaging model is quite nice. The IDL for the DOM is a little heavy as
it's geared toward Java/C++ implementations, but it looks like a
reasonably good Squeak interpretation wouldn't be hard, particularly if
we used Traits.
On top of that, I'd like to see a set of widgets with consistent
behavioural semantics, but variable appearance. This would give us
themes, which I think is a useful feature in a system as portable as
Squeak, as well as the NeXT-style NIB functionality that Todd
mentioned. A squeak version of InterfaceBuilder would be great!
I figure if we're going to put significant effort into user interface,
let's start fresh. The other approach of simply cleaning up the worst
areas of the existing Morphic code would be useful as well, but
anything in between would probably not be worth the effort.
Colin Putney
Simple. I think that all'y'all (thanks Jeff!) are barking up the wrong tree.
Look at eToys. Kids in fifth grade are able to master them even though
players have 500 methods IN ADDITION TO those in Morph and Object and the
kids are essentially dealing with all of them. Given this, isn't it time for
y'all to admit that you're more like these kids in fifth grade then one
might think?! ;-)
Or, put differently: The problem is not "cruft in Morphic", it isn't
"uglyness". That's an overly simplified point of view on the problem since
it didn't start out that way. It ended up there because of the way it was
exposed to all'y'all kiddies (including myself) not being able to protect
its own critical notions in any way.
To me, this is the logical result of many of the shortcomings of Smalltalk.
For example, subclassing for using the framework. When we subclass a
framework class like Morph in Smalltalk we do not *use* that class but
rather we *modifiy* it. We are able to access unprotected instance state
(look at the ridiculous comment in Morph>>bounds) we are able to override
system critical methods in incompatible ways. Even the tutorials (like the
one up at Squeak.org) teach you that it's okay to break the framework in
order to use it (in this concrete example, it lacks calls to any of the
"super" methods even though this is required by the framework).
Given this, any attempt to "clean up" Morphic is doomed to fail if it does
not address the "usability issues" for the programmer. The best you can hope
for is a temporary effect but any newbie can (and therefore will!) write
code that breaks the framework. Some of this code will either be cool or
needed enough so that someone else is going to use it and you'll end up
exactly where we are right now.
Documentation, tests, etc. will (while being useful) not solve this problem.
After all, we *want* people to play with this stuff, don't we? And if we
want people to play with it then it should be easy to use in "the right
way".
eToys do exactly this. They provide an environment which is extremely
convenient for the kids to use and boy, many a time I have wished that
Squeak would be more like eToys. Bridging this gap, making the "system
level" part of Squeak more like eToys is the thing that needs to be done. In
particular in the UI area since that's what most people will look at and
play with first, so it's the place where strong fences are most needed
(present in eToys). Pretty much everything else will come naturally. Of
course, such an environment would no longer be "Smalltalk" - but in order to
protect the critical system notions we simply HAVE to get rid of it or at
least some of its intrinsically dangerous notions when it comes to an open,
easily usable, and still robust environment.
Bottom line of this: Morphic is cool. Neither the designers nor the users
are to blame for where it ended up in Squeak. Squeak (Smalltalk) itself is -
by not providing any means to build the right "fences" into the framework.
So my essential feeling here is: If you want to fix Morphic, then fix
Smalltalk. Morphic is simply a place where it shows many of its intrinsic
shortcomings for all'y'all kiddies.
Cheers,
- Andreas
Hi Andreas,
Thanks for a very thought-provoking email!
With respect to the effort to cleaning up and documenting Morphic, I
think that it's great that people are willing to organize and do it.
Morphic is a really cool system. When people first see Squeak
Morphic, they are wowed because there's nothing much like it. But
Morphic hasn't reached its potential, because it's too large,
confusing, and (dare I say?) hacked together for the average Squeaker
to comprehend. I consider myself to have an above-average
understanding of Morphic, but the thought of adding (for instance)
tool-handling code to HandMorph is daunting. The concept itself is
quite simple, but HandMorph has so many dependencies (to and from)
that it is difficult to avoid breaking things.
Morphic will certainly not be the last word in direct manipulation
user interfaces. However, it certainly is better than anything else
that I'm aware of. Cleaning up Morphic will make it more accessible
to the uninitiated, and allow great applications to be written.
Current Squeakers will be happier, outsiders will notice the increased
happy glow, and will turn into happy Squeak newbies. The world will
be a happy and wonderful place.
Remember, not every Squeaker wants to be pushing the cutting edge of
HCI, nor dragged behind those who are (these social issues in the
Squeak community keep popping up everywhere :-). Also, the Squeak
community isn't a representative of the world population; although the
flaws in Morphic are real and it falls short of being a system that
Homer Simpson can use, many members of the Squeak list make a living
writing clean applications in languages much crappier than Smalltalk.
A clean core Morphic is clearly possible and desirable.
(deep breath)
On the other hand, I'm personally very excited about going beyond Morphic.
I have an inkling of what this would mean. Traits can be a very big part
of this, especially if you put some effort along the lines of Scott's
Vocabulary work. I don't have any particularly clear vision of how
this would work, though. Would you or Alan care to elaborate on how
you see things?
Thanks,
Joshua
It seems to me like there's several different Morphic topics being
discussed recently.
We're talking about (at least):
* Morphic itself: composition, liveness, event handling, stepping,
uniformity of interface, properties
* Standard user and programmer interactions with Morphs (halos, hand,
etc.)
* Things built with Morphic: widgets, other Morphs
* Using Morphic to build "standard" UIs (consistent widget sets, UI
builders)
* Support code for eToys and other environments like Wonderland and
Croquet (players, vocabularies, etc.)
* The programmer experience (how easy is it to build things)
* The learning curve (how easy is it to figure out how to build
things) (my preference is for steep (that is, quickly learned given
enough focus) learning curves)
* Morphic's documentation and tutorials
An example is not coding your new morph to report smaller damage areas
if it doesn't need to refresh the whole rectangle. I had written
quite a few morphs before I realized that could be done, and how to do
it. Documentation can help with this, but not fix it entirely. How
can we ensure that only code that follows the "proper" framework usage
makes it into the image, even assuming excellent documentation about
the hows and whys of the framework? The harvesters, already very busy,
would have to do much more than ensure that an enhancement didn't break
things. I don't think that this is feasible.
As a bit of an aside, I believe that we need to reify some of the UI
behavior. This actually fits in with Andreas's remarks about EToys
(the seed of the idea probably came from him). For example, if the
event handling logic was expressed as a state machine instead of
regular code (where the states are in instance variables), then it
would be easier to create an EToy-like system to modify the UI
behavior, while enforcing proper usage of the framework.
How would you do this? You can fix individual cases, but fixing
everything in a way that will stay fixed seems difficult. Right now,
many morphs call #changed to notify the system that they need to be
redrawn. This is straightforward for the programmer, but often
manifests itself as sub-optimal damage reporting. For example, in
your SelectionMorph example, when the size of the selection is
changed, the contents of the selection are recalculated, and #changed
is called by #setSelectedItems:. Ideally, you would only redraw the
morphs that entered/exited the selection, and the old and current
selection boundary. The problem is that #changed is called from other
places that really do require a full redraw. What solution can be
offered that provides the simplicity of calling #changed whenever you
change, without drawing more than necessary? I have no answer.
Bob,
> You don't even need the "equal" part of "separate but equal".
> Imagine a NewMorph as different as you like. Install trial
> versions of the NewMorphs in a NewWorldMorph. NewWorldMorph
> would be a bit special, appearing to its owner as a simple
> childless morph, but to its newSubmorphs it's a whole new
> world. NewWorldMorph>>drawOn: could dispatch whatever drawing
> messages NewMorphs supported and these NewMorphs could
> interact with their siblings, blissfully unaware of the old
> morphic world where your browsers and debuggers still live.
Exactly. It has the *huge* advantage that one can concentrate on those
issues that one really cares about without having to bother with building
the entire enchilada of tools first. And it's really simple - all you need
is the following:
-- event handling --
handleEvent: anEvent
"pass those events that we care about into our own environment"
| evt |
(#(mouseDown mouseUp mouseMove
keyDown keyUp keystroke) includes: anEvent type) ifTrue:[
"pass them into our own environment"
].
^super handleEvent: anEvent
handlerForMouseDown: anEvent
"answer self so we don't have to implement the entire wantsXYZ
stuff"
^self
handlesMouseOver: evt
"answer true"
^true
mouseEnter: evt
"grab keyboard"
evt hand newKeyboardFocus: self.
-- drawing --
drawOn: aCanvas
"draw my own 'display'"
aCanvas drawImage: myDisplay at: (bounds origin).
That's it. 5 methods all in all. With a morph that does the above you are
completely free to build any environment you want. You will get the events
from morphic and can dispatch appropriately. You can draw onto your own
"display" and do whatever you want in terms of graphics. If you want to
introduce stepping semantics you may also want to add #wantsSteps, #step,
and #stepTime and pass the step into your own environment.
Cheers,
- Andreas
Nothing personal, but as of now, this is the most frustating message I have
seen in this thread. Don't you realize that the Morphic that is perfect for
eToys *is* the one that is perfect for programmers?
I'll give you just one example: If I want to make a button in eToys, here is
what I do. I go to the supplies flap, grab a rectangle, open its viewer,
drag out the "color <- <...>" tile, set the color I want, set the trigger to
"mouse down", grab a second "color <- <...>" tile, change it to "mouse up"
and done.
If I want to make the same button using your "programmer's morphic" here is
what I needed to do: Go to the tools flap, open a browser, make a category
"My stuff" (never mind that I might have no idea where to go next after
opening the browser), figure out the thing I have to subclass (nevermind
that I might have never heard of Morph or RectangleMorph or whatever else),
define my class. Then I need to know what exactly this method is called that
I have to write - it's #mouseDown: (nevermind that I have no interest
whatsoever in the event which is passed along), then I need to write
something like:
self color: (Color r: 0.67 g: 0.43 b: 0.95).
(nevermind that I have to know these numbers, nevermind that I have to know
that I must not assign to the instance variable even though it happily
allows me to), then I implement a similar method for mouseUp:, then I need
to remember that this isn't enough - oh no, I still need #handlesMouseDown:
(as if the system couldn't figure out for itself). Now I have a class where
I have no idea whether it'll work or not. So then I open up a work space, go
in there type "MyButtonMorph new openInWorld" (nevermind that I have to
remember that weird message) and then I can finally see if I have made a
mistake or not.
So which way would you rather use yourself?! Unless someone has a really
masochistic inclination I can safely claim that the first one is the way to
go - it's better in every single way, starting from the ability to use
direct manipulation, over robustness, up to efficiency (even I need about
five times as long with the second one and I know *exactly* what I'm doing).
And this is exactly what I said in my message (and what frustrates me so
about what you write) - if anything we need *MORE* eToy-like things in
Squeak and and in particular at the system level. What you are proposing is
not just one, it's two steps backwards.
Cheers,
- Andreas
> >Why are Morphic Wrappers required for BobsUI?
>
>
> To keep the development and runtime functions seperate. When BobsUI
> windows run, they act like components written in any other dull
> business app building tool. You can't do things like bring up a halo
> or an inspector on them.
>
> However, during development, the developer needs halos, menus, and
> other interactive things, like the ability to drag and drop components
> onto windows. While it looks like the developer is manipulating the
> components, they are components wrapped in their own Morphic Wrappers.
> The halos, menus, and everything else you are using belong to the
> wrapper, not the component. So when you package a runtime production
> image, only the components, other necessary bobsUI code, and your
> objects, get packaged. What is in the wrappers isn't needed.
That's fascinating. This is exactly why I use Morph as a dynamic mixin in
my code. There are still some specializations needed, but it works out
beautifully (well, in my logic-test methods). Of course, in my mind,
Morphic without the wrappers isn't really Morphic at all... even Self's
Morphic seems deficient in comparison.