To: tnt Subject: calculated layout offset names --text follows this line-- Here are the results from the calculated layout design review. Proposal: ClassLayout offset method name backstop /offset % name => x y In a calculated layout specification, if a named offset is not one of the standard named corner position offsets (/North /East /West /South /NorthWest /SouthWest /NorthEast /SouthEast /Center) then an /offset message is sent to the object, which takes a name as an argument and returns an (x,y) offset from the object's lower left corner. The offset name will work the in both contexts that the corner position names may appear in calculated layout specifications. The proposal originally suggested sending the offset name to the object as a message, but it was changed to pass the name to the /offset method as an argument, instead. This is to avoid the proliferation of method names, to group all the offset calculations into one place for ease of documentation and maintenance, and so offset names can be like the standard corner position names, in mixed case without the "offset" suffix. The /offset method goes into ClassDrawable, as a SubclassResponsibility method. Subclassers overriding /offset should look them up in a dict or case statement, whose default case passes unknown names on to the super class. It is an error to refer to an offset not defined by a class or its superclasses. Examples: Normal calculated layout usage, positioning an object's west edge along the previous object's east edge (you can refer to the standard offsets just as before): /West {/East PREVIOUS POSITION} Positioning an object's north edge below a slider's minimum tick mark (you can refer to the documented offsets of toolkit components, for laying out adjacent labels): /North {/MinEndLabel /slider POSITION} Positioning a object's hot spot at the center of its parent (you can define offsets in your own classes by overriding the /offset method): /HotSpot {/Center PARENT POSITION} Where the object's /offset method might be defined as: /offset { % /Position => x y dup { /HotSpot { pop HotX HotY } /ColdSpot { pop ColdX ColdY } /WarmSpot { pop HotX ColdX add 2 div HotY ColdY add 2 div } /Default { /offset super send } } case } def Positioning an object's left sidebearing at the previous object's right sidebearing (user defined offsets can be used in both contexts where the standard offsets can appear): /LeftBearing {/RightBearing PREVIOUS POSITION} Proposal: Slider label positioning offset methods The names of the slider offsets have been changed to fit in with the new /offset naming scheme. Names recognized by the slider /offset method: /MinEnd (was /minoffset) At left end of hslider, or bottom of vslider. /MaxEnd (was /maxoffset) At right end of hslider, or top of vslider. /MinTick (was /mintickoffset) At left end below hslider, or top to right of vslider. /MaxTick (was /maxtickoffset) At right end below hslider, or bottom to right of vslider. These methods return the (x,y) offset from the slider's lower left corner to the point where a label should be justified. These offsets are useful for positioning labels and text fields relative to sliders in panels with calculated layout. Proposal: /addclientbefore /addclientafter There needs to be a way to add a client to the middle of a panel layout order, instead of appending it onto the end the way that /addclient does. This is especially important in calculated layouts to which you might dynamically add clients (i.e. in response to user input), which depend on the layout order for proper positioning. The /addclientbefore and /addclientafter names are pretty gross, so please suggest any better ones that occur to you. They are like /addclient except that they take an additional argument below the others on the stack, the name of the existing client adjacent to which to place the new client. These operators are meant to parallal /insertcanvasbelow and /insertcanvasabove, since bags contain groups of canvases, setting the canvas depth appropriatly, except that they also has to take into account the order of regions in the bag, as well. Proposal: ClassBag client names There is an easier way to do this. Forget about this implementation. There needs to be a way for a client to ask its parent bag for its name. There also needs to be a way to ask a bag for the names of its clients. These methods I am using in a subclass. They could be merged into ClassBag. /addclient { % name instance layout => - 3 copy pop exch /SetClientName self send /addclient super send } def /removeclient { % name => client true | false /removeclient super send { dup /TNTClientName undef true } { false } ifelse } def /SetClientName { % client name => - /TNTClientName exch put } def /ClientName { % client => name /TNTClientName get } def /clientnames { % - => [ key1 key2 ... ] [ ClientDict { pop } forall ] } def At the design review, it was agreed that a much easier way to get the name of a client was to use the "dictkey" utility on ClientDict. It's better to slow down the rare case of back mapping from client to client name, than slowing down the common case of addclient and remove client. It might be useful to have the above /clientnames method, and a /clientname method based on "dictkey". I tend to think it should be in ClassBag rather than left up to the application programmer, because the application programmer is interested in making these queries of the containing Parent canvas, and the application programmer is not always in control of the container class (as is the case with a ClassBaseWindow or panel container.) Proposal: ClassLayout /SEND enhancement This is not really necessary. Forget about it. There should be a convenient way to send a message to a named list element. /SEND { % ...args /method list-element => result... ResolveReference send } def Proposal: ClassLayout /SIZE enhancement This is not really necessary. Forget about it. Combination of the already existing methods: /WIDTH /HEIGHT /WIDTH and /HEIGHT can be redefined in terms of /SIZE /SIZE { % list-element => width height ResolveReference dup self eq InMinSize? and { pop 0 0 } { Size } ifelse } def /WIDTH { % list-element => width SIZE pop } def /HEIGHT { % list-element => height SIZE exch pop } def Proposal: Experimental panel souper-upper This is totally insane, forget I ever mentioned it. To avoid interobject sends, teach ClassPanel to access the position and size of canvas clients by hand. /Move { % x y client => - dup type /canvastype eq { movecanvas } { /move exch send } ifelse } def /Size { % client => width height dup type /canvastype eq { false getbbox 4 2 roll pop pop } { /size exch send } ifelse } def /Location { % client => x y /location exch send } def /CellSize { % - => 0 0 /regionclientlist self send { /size exch send 4 -1 roll max 3 1 roll max pause } forall /children self send { false getbbox 4 2 roll pop pop 4 -1 roll max 3 1 roll max pause } forall exch } def