In a previous question I asked how I could show the contents of a Dictionary
in a GUI. I started from this idea to build a GUI with a slightly better look and feel. It mainly consists of RectangleMorph
s glued together in columns and rows (cfr. the accepted answer in my previous question).
The problem now is that I would like my table to be updated when elements are added/removed/edited in my dictionary. I managed to write some Morph
that consists of columns of CellMorph
s, which inherit from RectangleMorph
and have model
and message
as instance variables with the following update message:
update
" update the contents of this cell "
| value |
self removeAllMorphs.
(model = nil or: message = nil)
ifTrue: [ value := '' ]
ifFalse: [ value := model perform: message ].
self addMorph: value asMorph.
As can be seen, the CellMorph
is a container for a Morph
containing the actual content of the cell. This works great for displaying the size of the dictionary for instance:
d := Dictionary new.
d at: 'foo' put: 100.
d at: 'bar' put: 200.
cell := CellMorph new
model: d;
message: #size;
color: Color white.
cell openInWorld.
d at: 'boo' put: 300. " cell will be updated "
but I don't seem to get something similar working for the contents of the dictionary, because I can't find a way to access single keys or values with a message. The only solution I can think of is to create new columns with new cells every time, but this is so expensive and I can't imagine that this is a good idea...
Therefore my question:
Is there a way to update my Morph
displaying the dictionary without creating billions of my CellMorph
s or should I forget about my idea and rather work with rows of CellMorph
s for instance in order to group the entries in the dictionary?
for completeness: the model:
message in CellMorph
looks like:
model: newModel
"change the model behind this cell"
model ifNotNil: [ model removeDependent: self ].
newModel ifNotNil: [newModel addDependent: self].
model := newModel.
self update.
update: aParameter
does nothing more than call update.
and I also added self changed.
in all messages of Dictionary
that I want the interface to be notified of (at: put:
, removeKey:
, etc.).
In the instance variable named 'message' you could have a Message
object, instead of having only the selector.
An instance of Message
has the receiver, selector and arguments. So, you could configure it with the dictionary keys sorted asArray
in the receiver, the selector #at: and an index, to get a specific key. Accessing the value would be getting the value at: that key in the dictionary.
I think that a Message is not executed with object perform: message
, you should check. message perform
should work because it already has the receiver.
In any case, this complexity may show that having only (one) model and (one) message is not enough to get the model in th granularity you want, and you can possibly specialize a bit more, using the knowledge that the model is a dictionary. For instance, having an instance variable for key or for keyIndex.
Some side notes about the code:
(model = nil or: message = nil)
has comparisons with nil, that can be replaced by #isNil message or, if you want to stick with equality, use the faster == to compare identity, since nil is unique.
#or:
is used to get the benefits of partial evaluation (the argument is evaluated only if the receiver is false). But that only works if you have a block as argument, otherwise the expression is evaluated before, to get the argument for the message in the stack.