Hi Brian, I remember seeing some tabbing support built into Morphic. I'm working on a UI utility in Morphic and ended up wrapping a TextMorph with my own "EntryFieldMorph" objects that end up controlling the TextMorph for me. It was easy-enough with Morphic to implement EntryFieldMorph>>#handleKeystroke: and check the incoming KeyboardEvent's keyCharacter for Character tab, as in..
handleKeystroke: aKeyboardEvent | keyPressed | aKeyboardEvent wasHandled ifTrue: [ ^self ]. keyPressed _ aKeyboardEvent keyCharacter. keyPressed = (self preferred: #keyForFocusShift) ifTrue: [ ^self shiftFocus: aKeyboardEvent ]. keyPressed = ...
I don't see any harm in case-logic for checking hot keys..
A one liner to shiftFocus:
aKeyboardEvent hand newKeyboardFocus: self nextTabStopMorph
On Tue, 26 Oct 2004 19:26:02 -0700 (PDT), Chris Muller afu.nkyobject@yahoo.com wrote:
I don't see any harm in case-logic for checking hot keys..
I've been meaning to ask the group this, since I remember picking up the anti-case message from Meyer (coincidentally enough, in his Eiffel books) but I'm trying to figure out how I'd program a state machine without it. Well, I can see how to do it, but I don't think I "get it" since it doesn't seem easier or clearer to do it that way.
Can anyone recommend some state-machine type stuff in the codebase for me to look at? Or thoughts on how you'd code something like the code below?
(Some background: I'm writing a book for kids on programming. I learned to program in Basic and despite the fact that it gave me a strong taste for finding better ways of doing things, I really don't want to inflict that on another generation.<s> That means, in part, rooting out my own bad habits so as not to pass them on.)
===Blake===
WHILE (i<=len(expr)) IF c>="A" and c<="Z" THEN 'we're at a function call ... ELSE IF C="[" THEN 'we're at a user defined variable ... ELSE IF C="_" THEN 'we're at a system value ... ELSE IF at("operator", expect)>0 and at(C, "+-*/<=>")>0 THEN 'operator ... ELSE IF at(c+d, ".A.O.N") > 0 THEN 'logical operator ... ELSE IF at(C, numeric)>0 or ((C="-" .or. D="+") and at(D, numeric)>0) THEN 'we're at a literal constant ... ELSE IF C = "," THEN ... ELSE IF C="(" THEN 'we're grouping terms or in a procedure ... ELSE IF C=")" THEN 'we're ending a group of terms ... ELSE IF C=" " THEN 'ignore spaces ... END WHILE
Am 27.10.2004 um 07:15 schrieb Blake:
On Tue, 26 Oct 2004 19:26:02 -0700 (PDT), Chris Muller afu.nkyobject@yahoo.com wrote:
I don't see any harm in case-logic for checking hot keys..
I've been meaning to ask the group this, since I remember picking up the anti-case message from Meyer (coincidentally enough, in his Eiffel books) but I'm trying to figure out how I'd program a state machine without it. Well, I can see how to do it, but I don't think I "get it" since it doesn't seem easier or clearer to do it that way.
Can anyone recommend some state-machine type stuff in the codebase for me to look at? Or thoughts on how you'd code something like the code below?
Parser >> scanToken maybe?
(Some background: I'm writing a book for kids on programming. I learned to program in Basic and despite the fact that it gave me a strong taste for finding better ways of doing things, I really don't want to inflict that on another generation.<s> That means, in part, rooting out my own bad habits so as not to pass them on.)
===Blake===
WHILE (i<=len(expr)) IF c>="A" and c<="Z" THEN 'we're at a function call ... ELSE IF C="[" THEN 'we're at a user defined variable ... ELSE IF C="_" THEN 'we're at a system value ... ELSE IF at("operator", expect)>0 and at(C, "+-*/<=>")>0 THEN 'operator ... ELSE IF at(c+d, ".A.O.N") > 0 THEN 'logical operator ... ELSE IF at(C, numeric)>0 or ((C="-" .or. D="+") and at(D, numeric)>0) THEN 'we're at a literal constant ... ELSE IF C = "," THEN ... ELSE IF C="(" THEN 'we're grouping terms or in a procedure ... ELSE IF C=")" THEN 'we're ending a group of terms ... ELSE IF C=" " THEN 'ignore spaces ... END WHILE
- Bert -
On Wed, 27 Oct 2004 11:28:59 +0200, Bert Freudenberg bert@impara.de wrote:
Parser >> scanToken maybe?
Thanks!
All right, I found the parser class but no "scanToken" message. Too bad there's no class comment! <duck>
Anyone know of code that uses the parser class?
On Wed, Oct 27, 2004 at 02:55:41AM -0700, Blake wrote:
On Wed, 27 Oct 2004 11:28:59 +0200, Bert Freudenberg bert@impara.de wrote:
Parser >> scanToken maybe?
Thanks!
All right, I found the parser class but no "scanToken" message. Too bad there's no class comment! <duck>
Try Scanner>>scanToken
It even has a class comment :)
Dave
On Wed, 27 Oct 2004 05:59:58 -0400, David T. Lewis lewis@mail.msen.com wrote:
Try Scanner>>scanToken
It even has a class comment :)
Groovy.
Blake, the simple answer is to use a separate class per state. Then instead of a case statement, you can send messages to the current state object and ask it to Do The Right Thing. Since each state has a different class, each one can have a separate doTheRightThing method, and the normal OO method-binding machinery will hook up the doTheRightThing *request* with the appropriate *response*.
To be fair, a lot of people do it the other way too, though, and use symbols plus case statements.
I don't think both are fine in the right circumstances. If you have just a few states and only a few places that the state is checked, then case statements plus symbols can give you shorter, simpler code; converting the simple cases to use a class-per-case means that anyone reading the code has to go tracing through the state classes all the time. On the other hand, if you check the state in a lot of places, or if there are a lot of differernt states, then using first-class state objects, each with their own class, can make the code simpler on the whole. In this latter case, readers cannot asborb all the different states simultaneously anyway, so you may as well factor them out.
Finally, notice that separating out the states into separate objects, may make it easier for you to think about what exactly a state is. This can make it easier to factor your state into multiple objects, thus reducing the number of explicit states. Having a lot of explicit, discrete states seems like a recipe for confusion, so trying to minimize them seems like a good idea.
Lex
On Wed, 27 Oct 2004 11:34:27 -0400, lex@cc.gatech.edu wrote:
To be fair, a lot of people do it the other way too, though, and use symbols plus case statements.
Lex,
The thoughtful reply is much appreciated. Also good to know that I'm not completely alone. I have done it the OO way, though recently I've been doing it with case statements. I probably should go back and do a few opposite of the way I did it originally and see what I learn.
Along similar lines, Meyer expressed disapproval for enumerated types, yet they're one of the few things I consistently miss (in languages that are not Pascal<s>). It still seems like it would be a useful thing to be able to describe a sequence of values--particularly specific to a class (which has always been the problem with Pascal enums)--and even named constants.
===Blake===
Blake blake@kingdomrpg.com wrote:
Along similar lines, Meyer expressed disapproval for enumerated types, yet they're one of the few things I consistently miss (in languages that are not Pascal<s>). It still seems like it would be a useful thing to be able to describe a sequence of values--particularly specific to a class (which has always been the problem with Pascal enums)--and even named constants.
An issue with enumerated types is that they are hard to extend. What if you make a subclass, and want to have an extra choice available in the enumerated type? With raw symbols this is fine. With class-per-state this is fine. But with a Pascal-like or C-like (or ML-like!) enumerated type, you can only extend it by editing the original definition.
<shameless plug> Chuck is good at finding all the symbols that are available in an enumerated type. Point to a variable which holds the Squeak equivelant of an enumerated type, and, if Chuck can figure out what the code is doing, the type of that variable will be the list of possible symbols. Given that such tools are possible, it seems less important that the programmer maintain an explicit list of options anywhere. </shameless plug>
-Lex
On Thu, 28 Oct 2004 13:47:45 -0400, lex@cc.gatech.edu wrote:
An issue with enumerated types is that they are hard to extend. What if you make a subclass, and want to have an extra choice available in the enumerated type? With raw symbols this is fine. With class-per-state this is fine. But with a Pascal-like or C-like (or ML-like!) enumerated type, you can only extend it by editing the original definition.
I feel like saying "And this is =my= problem somehow?" but:
-> I would be happy just to have it private to a class.
-> If it were exposed, why would extending it be so challenging? They're just integers underneath, eh? I guess there'd be some issues with passing an enumerated type. Hmmm.
Chuck is good at finding all the symbols that are available in an enumerated type. Point to a variable which holds the Squeak equivelant of an enumerated type, and, if Chuck can figure out what the code is doing, the type of that variable will be the list of possible symbols.
What is Chuck? What is the Squeak-equivalent of an enumerated type?
An issue with enumerated types is that they are hard to extend. What if you make a subclass, and want to have an extra choice available in the enumerated type? With raw symbols this is fine. With class-per-state this is fine. But with a Pascal-like or C-like (or ML-like!) enumerated type, you can only extend it by editing the original definition.
I feel like saying "And this is =my= problem somehow?" but:
-> I would be happy just to have it private to a class.
-> If it were exposed, why would extending it be so challenging? They're just integers underneath, eh? I guess there'd be some issues with passing an enumerated type. Hmmm.
I meant in Pascal or C. In these languages, there is a single declaration that lists all the possibilities. To add more possibilities you have to modify that declaration. These languages don't have subclasses, so it is not a big deal. I wonder what C++ does for this, though? Can a C++ subclass add items to an enumerated type its superclass uses? I bet not -- figuring out what it would mean is a headache!
In a Smalltalk (or Java) enumerated type, there's no declaration at all. You are right that you can just use more codes whenever and wherever you like.
Chuck is good at finding all the symbols that are available in an enumerated type. Point to a variable which holds the Squeak equivelant of an enumerated type, and, if Chuck can figure out what the code is doing, the type of that variable will be the list of possible symbols.
What is Chuck? What is the Squeak-equivalent of an enumerated type?
Chuck is a project to help people browse Smalltalk code. Its browser lets you point to a variable and say "what is your type", among other things. The type system of Chuck includes types like "#foo or #bar or #baz", ie lists of symbols. And that's my answer to your second question: I think that lists of symbols would be the Squeak-equivalent of an enumerated type.
Chuck is on SqueakMap, and has this home page:
http://www.cc.gatech.edu/~lex/chuck/
-Lex
On Sun, 31 Oct 2004 13:47:06 -0400, lex@cc.gatech.edu wrote:
I meant in Pascal or C. In these languages, there is a single declaration that lists all the possibilities. To add more possibilities you have to modify that declaration.
True. But necessary? (Maybe.<s>)
These languages don't have subclasses, so it is not a big deal. I wonder what C++ does for this, though? Can a C++ subclass add items to an enumerated type its superclass uses? I bet not -- figuring out what it would mean is a headache!
No, in both C++ and Object Pascal, the enumerated types are unchanged from their ancestral non-object forms.
Chuck is a project to help people browse Smalltalk code. Its browser lets you point to a variable and say "what is your type", among other things. The type system of Chuck includes types like "#foo or #bar or #baz", ie lists of symbols. And that's my answer to your second question: I think that lists of symbols would be the Squeak-equivalent of an enumerated type.
Chuck is on SqueakMap, and has this home page:
'k. I'll have to check Chuck out.
Lex wrote:
Blake, the simple answer is to use a separate class per state. Then instead of a case statement, you can send messages to the current state object and ask it to Do The Right Thing. Since each state has a different class, each one can have a separate doTheRightThing method, and the normal OO method-binding machinery will hook up the doTheRightThing *request* with the appropriate *response*.
Sounds way too OO for me :^)
On Tuesday 26 October 2004 10:15 pm, Blake wrote:
Can anyone recommend some state-machine type stuff in the codebase for me to look at? Or thoughts on how you'd code something like the code below?
Yes. In my Connectors 2 package (available on SqueakMap) there is code for a hierarchical state machine (supporting the full Harel semantics), and a Morph class that uses these state machines for event handling.
You can add to or modify existing state machine definitions in two directions: you can define sub-states to provide specialization, and/or you can inherit from existing event handler classes and override their event handling methods.
The stock QHsmMorph has the following state hierarchy. Note that it does not use the MouseClickState state machine, handling click and double-click detection in its own handlers.
Top Global WaitingForDoubleClick FirstClickUp (MouseDown) WaitingForDoubleClickWithMouseDown SecondClickDown (MouseUp) FirstClickDown (MouseUp) Idle Dragging Initial
Events that occur in a sub- (nested) state like FirstClickUp can be handled in the state handlers for FirstClickUp, its parent WaitingForDoubleClick, or its parent Global. There is one method per state. Optionally, you can specialize by providing a method for a given kind of event in a given state. There are three examples of this here (they are shown by parentheses).
This is based on the idea (strange as it seems) of generating method selectors based on the current state and maybe also the type of the event.
So in this case, the corresponding method selectors are:
Top => stateTop Global => stateGlobal WaitingForDoubleClick => stateWaitingForDoubleClick FirstClickUp (MouseDown) => handleMouseDownInFirstClickUp WaitingForDoubleClickWithMouseDown => stateWaitingForDoubleClickWithMouseDown SecondClickDown (MouseUp) => handleMouseUpInSecondClickDown FirstClickDown (MouseUp) => handleMouseUpInFirstClickDown Idle => stateIdle Dragging => stateDragging Initial => stateInitial
Each event has a symbolic type and an optional event (perhaps a MorphicEvent) associated with it.
When an event is handled, first we make a specialized selector using the symbolic type of the event pasted together with the current state name:
'handle' + <event type> + 'In' + <current state name>
If there is no interned symbol matching that, or if the event handler does not understand that selector, then we try the more general state handler:
'state' + <current state name>
If neither selector matches, there is a catch-all state handler for unhandled states.
As goofy as this sounds, it results in a clean separation of state handling code.
Of course, I sometimes end up with a case switch inside the state handler methods to handle the individual event responses. Here's a snippet from the above QHsmMorphEventHandler (event is an instance variable that holds the MorphicEvent being handled):
stateDragging: evt ^evt caseOf: { [#entry] -> [self startDrag: event. nil]. [#exit] -> [self stopDrag: event. nil]. [#mouseUp] -> [ self returnToIdleHistory ]} otherwise: [self state: #Global]
It's possible to hold states in variables, like here where we return to history:
returnToIdleHistory "Idle history has been saved; transition back to it (and clear it)" | history | history _ idleHistory ifNil: [ #Idle]. idleHistory _ nil. mySource ~= myState ifTrue: [self log: #newState items: {myState stateName. mySource stateName. history. 'idle history' }] ifFalse: [self log: #newState items: {myState stateName. history. 'idle history'}]. ^self newDynamicState: history.
The return value from the state handlers is either nil if there is no change in state, or a QState object representing the new state when a transition is desired.
Hope this helps,
squeak-dev@lists.squeakfoundation.org