3: Window Manager
Window Managers are what holds windows together. Windows are just a bunch of individual sprites. When you drag a window, it looks like you are dragging a single entity. But you are dragging a collection of sprites around. See scenes 2 and 3 of the demo. When you drag a window around and then open up a child window of that window, note that the child window has moved also (if the window is so configured). When you close a parent window, the open child windows of that window close also. See scene 4 of the demo. This is the work of the Window Manager. The "3: Window Manager" behavior is crucial to the creation of multi-sprite objects in WFS.
Every window must have a Window Manager. A Window Manager consists of
The "3: Window Manager" behavior has code in it that does not allow it to be attached to anything but the "2: Window Manager" bitmap.
See the "2: Window Manager" bitmap.
When you drop the "3: Window Manager" behavior on a sprite
whose member is the "2: Window Manager" bitmap, the following
dialog box opens. Click on the graphic to go to documentation
on the particular parameter. And then click on the triangle to come
back to this graphic.
Names of multi-sprites should be unique across the entire movie. Names of multi-sprites can consist of any string of ASCII symbols with one exception: don't put any "+" symbols in names. They are used in a special way in dynamic multi-sprite names, which are automatically generated by WFS to ensure uniqueness regardless of how many dynamic copies of the multi-sprite are made.
Multi-sprites add their name and spriteNum to the global gWFSMultiSpriteList list of instantiated windows and menus. They also delete this entry on endSprite, ie, when they end.
WFS prompts you to rename a particular multi-sprite if there is a name conflict with another instantiated multi-sprite.
The way I name mine is as follows. Suppose the movie consists of scenes separate in the Score. The first multi-sprite in scene one is named "1.1". The second multi-sprite in scene one is named "1.2". The first multi-sprite in scene 2 is named "2.1". And so on. But, you know, whatever gets you through the movie so that they're named uniquely will be fine with WFS.
Being able to determine the name of a Manager you created some time ago is important to when you drop certain behaviors on sprites, such as the "7: Open a Window" behavior, which prompts you for the name of the window you want to open.
How do you find out the name of a Manager if you forget it? Instructions are below the graphic.
To change or view the parameters of a Window Manager or Menu Manager,
Alternatively, you can scroll through the parameters as diplayed at the bottom of the Property Inspector.
Note for Programmers: if you know the spritenum of the manager, wfsGetManagerName will return the name. If you know the name of the manager, wfsGetManagerSpritenum will return the spritenum, as will wfsManagerIsInstantiated, which is used both to get the spritenum of the manager and test whether a manager is instantiated.
If you check this box, the window will be visible after the playback head enters the first frame in which the Window Manager and its elements are instantiated and the Manager and the Elements automatically execute their 'on beginSprite' handlers.
Typically, you want the main window to be visible when it comes into existence. It's a good idea to make everything part of some window or menu, though you don't have to. Typically, child windows are not visible initially. Such as windows that appear after a menu selection is made.
If this box is checked, then the window will be centered on beginSprite. Otherwise, it will appear in the position it appears in while authoring.
Some types of windows, like the typical "Save As..." modal dialog box you have seen in programs many times, do not let you access the rest of the program until you deal with the dialog box. If you set the slider in the above diagram to a non 0 value, you will create a window that functions as a modal dialog box, and everything that isn't part of the window will be dimmed by a percentage corresponding with that non 0 value. A value of 1 results in a non-noticeable dimming while a value of 100 makes everything behind the window invisible until the window is closed.
This is the only difference between modal dialog boxes and regular windows in Windows For Shockwave: the slider has a non 0 value. If you want to create a regular window, a non-modal window, set the slider to 0. See scene 2 of the demo for an example of a modal window. The slider of its Window Manager was set to 20, so what is behind it is slightly dimmed.
Modal dialog boxes usually have tabs and radio buttons and so on, in applications. Windows For Shockwave does not support the creation of such controls for dialog boxes, though they aren't hard to create yourself. Projectors do, however, support the creation of sophisticated dialog boxes via the MUI Xtra.
If you check this box, then the window will be brought to the front when you click it while the movie is playing if it is behind something else. Normally you want this checked.
If this is checked, then when the window is brought to front, the open child multi-sprites will also be brought to front. The child multi-sprites will be in front of the window.
Children are normally in front of their parents. For instance, dialog boxes are child windows of the window from which they arise, and they are always in front of the window.
If this box is unchecked, then when the window is brought to front, open child multi-sprites are not brought to front.
A blank value indicates that the window has no parent.
Multi-sprite parent-child relationships are concerned with what other multi-sprites disappear when you close them. When you close a parent window that has child windows that are open, the children windows close also, although when you close the children, the parent does not close. Parent-child window relationships can also be used to make a family of windows behave visually as a unit, as indicated in the next paragraph.
Static child windows must be lower in the Score than their parent windows. Just like window elements are lower in the Score than their Window Manager. The reason that static child windows must be lower in the Score than their parents is that child windows add their Manager's spriteNum to a list of child windows maintained by the parent on beginSprite. If the child is higher in the Score than the parent, then that list does not yet exist when the child tries to add itself as a child to the parent.
When this box is checked and the window has a parent, then when the parent is dragged, so is this window, whether it is currently visible or not.
This enables being able to drag multiple windows around at once, having them behave as one 'family' where the parents drag the children around. Not vice versa. The parents are the boss in Windows For Shockwave you kids. Sorry.
Do not check this box if the window has no parent. It has no effect, in this case, however.
A frequently asked question is 'How do I make one window be part of another window?' The answer is that you make one window a child of the other window and you check the box we are currently discussing. Parent-child relationships between windows are basically a way of joining multi-sprite objects.
In scene 4 of the demo, the top three little gray windows are configured to move with their parent. Window 4.5 isn't, however, although its parent is window 4.4. So 4.5 doesn't move with its parent. But when 4.4 is closed, 4.5 closes because the parent of 4.5 is 4.4.
MOVING MENUS, FAMILIES, & ALIGNMENT
This section is for programmers who want to do things beyond what the WFS drag and drop behaviors permit you to do. The "3: Window Manager" behavior contains the below public handlers that are useful for opening, closing and changing the locZ of windows. The public handlers also permit developers to move windows and also families of windows around and to align them.
The public handlers also provide handlers for setting parent-child relationships between Managers. These last handlers are the most powerful of the public handlers. Why? Because they go beyond the notion of windows and menus. WFS is actually not so much about windows and menus as multi-sprite objects. Being able to set parent-child relationships amongst multi-sprite objects allows you to combine multi-sprite objects to make larger objects.
To see working code involving these handlers, open up the source DIR of the demo and the script window in Director, and do a search on the name of the handler.
OPENING, CLOSING, AND LOCZ OF WINDOWS
This opens the window whose Manager's spriteNum is the integer managerSpriteNum and brings it to front. This handler is called by any WFS behaviors that open windows.
You can call wfsOpenWindow via either of the ways shown below. The advantage of the second way is that if the manager is not actually instantiated, no script error will result. That is the only (but notable) difference between using sprite(x).whatever() and sendSprite(x, #whatever).
This brings the window to the front. It is called when the window is opened. It's also called when you click on a Window Element and you have the window configured to come to front when clicked. Even if you don't have it so configured, this handler is called when you click an Element that has the "6: Handle" behavior attached to it. This handler, by default, also brings the children to front--in front of the current window. That is when doNotBringChildrenToFront is not specified or is VOID. The idea is that children stay in front of their parents. If you call this handler with a positive integer parameter for doNotBringChildrenToFront, however, the children will not be brought to front.
The above also brings the children to front,but the below does not.
The above brings the window to front but does not bring the children to front.
The reason the parameter works this way is for backward compatibility. Version 3 did not have a parameter. The reason the parameter was introduced was to make it possible to drag a parent window over its children. But usually you don't want this to happen, ie, usually you want it to behave as in version 3, which it does if you do not pass a positive integer parameter.
Version 4.5 makes use of this parameter in this new feature.
This handler closes the referenced window and all its open descendents (not just children). It restores the original locZ values and makes the window invisible. You do not need to call this handler if you use either the 'Close My Window' behavior or the 'Close A Window' behavior. They call this handler.
This restores the LocZ of each sprite in the window. It is called in the wfsCloseWindow handler.
MOVING WINDOWS, FAMILIES OF WINDOWS, AND ALIGNMENT
To see these handlers used in working code, open up the feature tour source code in Director and search for the handler name.
This handler moves the referenced window to the center of the stage, and moves descendant windows (that are configured to be moved with their parents) accordingly, not necessarily centering them, but moving them so that their position relative to the centered window is preserved.
This handler first calculates the current position of the window. But what is that, since the window is really just an aggragate of sprites with possibly different locations? So it is a bit arbitrary: it defines the position of the window as the left (horizontal) and top (vertical) values of the first window element (not the window manager). Hence it is a good idea to make that window element be the top leftmost element in the window since, by convention, that is generally what we mean when we move something to a point. Note that the Window Manager sprite is not moved.
deltaPoint is a parameter of type Point. This handler moves the referenced window and its descendent windows (that are configured to be moved with their parents) by deltaPoint.locH pixels horizontally and deltaPoint.locV pixels vertically. These can be negative or positive integers or 0. This handler is recursive; it moves descendants, not just children.
absolutePoint is a parameter of type Point. This handler moves the top left point of this window to absolutePoint and moves all its descendent windows (that are configured to be moved with their parents) accordingly so that they are in the same position relative to the referenced window. The top left point of this window is considered to be the top left point of the second window element of the window. This handler is recursive; it moves descendants, not just children.
absolutePoint is a parameter of type Point. This handler moves this window to the point specified by absolutePoint. This handler first calculates the current position of the window. But what is that, since the window is really just an aggragate of sprites with possibly different locations? So it is a bit arbitrary: it defines the position of the window as the left (horizontal) and top (vertical) values of the first window element (not the window manager). Hence it is a good idea to make that window element be the top leftmost element in the window since, by convention, that is generally what we mean when we move something to a point. Note that the Window Manager sprite is not moved.
deltaPoint is a parameter of type Point. This handler moves the referenced window and all its window elements by deltaPoint.locH pixels horizontally and deltaPoint.locV pixels vertically. These can be negative integers as well as positive or 0. It doesn't move the Window Manager sprite.
elementSpriteNum is an integer parameter indicating the spritenum of the window element to center horizontally. This handler checks where the regpoint is in elementSpritenum and centers the element horizontally relative to the window's background, which is assumed to be the first window element in the window (after the Window Manager, which isn't considered to be an element of the window).
elementSpriteNum is an integer parameter indicating the spritenum of the window element to center vertically. This handler checks where the regpoint is in elementSpritenum and centers the element vertically relative to the window's background, which is assumed to be the first window element in the window (after the Window Manager, which isn't considered to be an element of the window).
This handler positions the locv of WindowElementSpritenum theLocv pixels below the window's background, which is assumed to be the first window element (after the Window Manager).
This handler positions the locV of windowElementSpritenum at theLocV.
This handler returns the name of the parent or "" if it has no parent.
This handler returns the spritenum of the parent or 0 if it has no parent.
See scene 9 in the demo for an example of this handler.
Almost like people, a multi-sprite can have many children but, at most, one parent.
theChild parameter is an integer or string. If it is a string, it denotes the name of a multi-sprite. If it is an integer, it denotes the spriteNum of a manager of a multi-sprite.
moveChildWhenIMove is a boolean. If it is TRUE, then the child will move when the parent is moved. If it is FALSE, then the child will not be moved when the parent is moved.
wfsSetChild returns 1 (TRUE) if the operation was successful, and 0 (FALSE) if the operation was not successful and was cancelled. If wfsSetChild returns 0, that means that the child was identical to the parent (a child cannot be its own parent) or the child or the parent were not instantiated or theChild spriteNum <=0 or parentManagerSpritenum < 0.
We see in the below diagram that a call to wfsSetChild is equivalent to a call to wfsSetParent, only with the parameters reversed. The moveChildWhenIMove parameter has been omitted to clarify the main point. The "I" in 'moveChildWhenIMove' refers to the parent.
A Manager is not allowed to be among its own ancestors. In other words, a Manager cannot be its own parent or grandparent etc. It would cause infinite loops when you move or close families of multi-sprites. The diagram below illustrates how wfsSetChild handles such a situation.
What we see in the above diagram is how the call sprite(G).wfsSetChild(B) is handled. It does indeed result in B becoming a child of G. But the parent of B is changed to be G (as you would expect) and, also, D is no longer a child of B. The relation between B and D has had to be severed, or B would become its own granddaddy. The above operation returns 1. The moveChildWhenIMove parameter is omitted from the above diagram to make the main points clearer.
This handler sets the child's data and also the parent's data, so that if you want to set a child, this is the only call you need to make.
Note that if the child already has a parent, this handler changes the parent. A child can have, at most, one parent though a parent can have multiple children.
See the included .DIR for example usage of wfsSetChild and wfsSetParent.
This handler deletes theChild from the Manager whose spriteNum is parentManagerSpriteNum (see below), ie, it makes the child no longer a child of the parent and sets the child so that it has no parent.
This handler returns 1 if the operation was successful, 0 if the operation was not successful and the operation was cancelled. It will return 0 if theChild is not instantiated or is not a child of the parent.
theChild can be a string or an integer. If it is an integer, it refers to the spriteNum of the Manager of the child. If it is a string, it refers to the name of the child.
This handler adjusts data in the child and in the parent Manager, so if you want to delete a child, this is the only call you need to make.
See scene 9 in the demo for an example of this handler.
theParent is a string or integer. If it is a string, it is the name of the proposed parent window or menu. If it is an integer, it is the spriteNum of the proposed parent window or menu's Manager.
If you want to set the current window or menu to have no parent, then use theParent="" or theParent=0. Or call wfsDeleteChild.
MoveWhenParentMoves is a boolean indicating whether the current window should be dragged when its proposed new parent is moved.
A call to wfsSetParent is equivalent to a call to wfsSetChild, only the parameters are different. In other words, if you make window A the parent of window B, that's the same as making window B the child of window A. Use whichever one you like. See the documentation (above) on wfsSetChild. It is more detailed than the documentation on wfsSetParent. In particular, look at the diagrams.
wfsSetParent returns 1 if the operation was successful, 0 if the operation was not successful and was cancelled. A Manager is not allowed to be one of its own ancestors.
A Manager can have, at most, one parent. If the Manager with spritenum=managerSpritenum already has a parent, then wfsSetParent makes theParent the sole parent.
This handler sets data in both the managerSpritenum and theParent. You don't need to call any other handlers to set the parent.
This returns an integer indicating the number of children of the multi-sprite managed by sprite managerSpriteNum.
This returns a linear list containing the spritenums of the managers of child multi-sprites. This list is a duplicate of pWFSChildList, a property of each manager. Since the list is a duplicate of pWFSChildList, changing it does not change the status of the multi-sprite's children.
If theBoolean is TRUE (or 1) then wfsSetMoveWithParent sets the manager to move when its parent moves. If theBoolean is FALSE (or 0) then wfsSetMoveWithParent sets the manager to not move when its parent moves.
Returns 0 if there is no parent or it is not instantiated. Even so, should the manager acquire a parent at a later time, wfsSetMoveWithParent(1) sets it so that it will indeed move with its new parent, should it acquire one.
Returns 1 otherwise.
MISCELLANEOUS PUBLIC HANDLERS
Returns a duplicate of the element list, a linear list that contains the spritenums of the elements of the multi-sprite. The first entry in the list is always the spritenum of the manager of the multi-sprite, which you shouldn't move.
Finds an element or all elements named theName (within a particular multi-sprite). If returnList=VOID (or is not specified), then the handler will search for the first occurrence amid the elements of the multi-sprite for an element named theName. If returnList<>VOID, then the handler will search for all occurrences of elements named theName (within a particular multi-sprite). This handler does not look at the names of managers. It looks at the names of elements, which in this context are never managers.
DMX 2004 lets you name sprites and channels. I looked into those features to see if I could use them in WFS. But the Director features have problems: they have to be set during a score recording session (which screws up dynamic sprites), and dynamic sprites cannot have sprite names. So I thought I'd implement a WFS version that I think is far more flexible and useful. It's important to note that the WFS element naming system is completely independent of the built in Director channel and sprite naming system. For instance, if you have named a channel "bobo", that doesn't mean that the WFS element in that channel is named "bobo".
Also, the names of elements are not the same as the names of Managers. When you drop the "3: Window Manager" behavior on a sprite, you must name the Manager. When you drop the "4: Window/Menu Element" behavior on an element, you don't need to name the element if you don't want to. Also, Manager names have to be unique among instantiated Managers. There is no such constraint concerning the names of elements. You can have as many repeats in the names of instantiated elements as you like.
If returnList=VOID (or is unspecified), then returns an integer indicating the spritenum of the first element in the multi-sprite named theName. If there is no element named theName, then the handler returns 0. If returnList <> VOID, then returns a linear list indicating the spritenums of all elements in the multi-sprite named theName. Returns the empyty list [ ] if there are no elements named theName.
theName: A string denoting the name of the element
returnList: is an optional parameter. If returnList=VOID or is unspecified, then the handler will look for only the first occurrence of an element in the multi-sprite named theName and the handler will return an integer. If returnList <> VOID, then the handler will return a linear list indicating the spritenums of elements in the multi-sprite named theName.
Suppose that sprite 5 is a Manager of a multi-sprite, and that two of its elements, sprites 9 and 11, are named "bobo".
will return 9
will return [9, 11]
Suppose that, below, you want to find the spritenum of an element named "bobo" which you know is part of an instantiated multi-sprite named "Alice".
You set the name of elements when you drop the "4: Window/Menu Element" behavior on a sprite and you can change the name at run time. That behavior contains two handlers relevant to element names: wfsGetElementName and wfsSetElementName.
The "1: prepareMovie" script contains a handler also named wfsGetElementNamed. It will search multiple multi-sprites for elements with a specified name (in various ways). This is a bit different from the current handler, because the current handler searches one particular multi-sprite.
This returns a boolean that indicates whether the referenced menu is currently open. Open is TRUE.
This returns the rect of the window. What is that? It is the rect of the background element, which is the first element below the manager. If the pWFSElementList contains only one entry, 0 is returned.
The background of a multi-sprite is, by WFS convention, defined as pWFSElementList. In other words, it's the element right after the manager. If pWFSElementList contains only one entry, 0 is returned.
The "3: Window Manager" behavior contains many public handlers. These handlers are called by other sprites that wish to perform some operation on a window.
For instance, if we look at the code of the "5: Close My Window" behavior, in the "wfsDoMeNow" handler, you see that it makes a call to wfsCloseWindow.
on wfsDoMeNow me
--This checks to make sure that the sprite has a copy of the
--"4: Window/Menu Element" behavior attached to it and opens
--an alert box, for debugging purposes, if it doesn't. If the
--"4: Window/Menu Element" behavior is attached to the sprite,
--then we check to see if the element's manager is instantiated.
--If it is, then we close the window.
b=offset("4: Window/Menu Element", a)
if b=0 then
--Then it doesn't have a copy of the "4: Window/Menu Element" attached to it.
alert("Sprite " & string(spritenum) & " needs a copy of the '4: Window/Menu Element' behavior attached to it since it has the '5: Close My Window' behavior attached to it.")
--Else the sprite does have the "4: Window/Menu Element" behavior
--attached to it, which stores pWFSManagerSpriteNum.
if wfsManagerIsInstantiated(myWindowManager) then
--Then the manager is instantiated, so it is closable.
sendSprite (myWindowManager, #wfsCloseWindow)
alert("Sprite " & string(spritenum) & " is trying to close the window managed by sprite" & string(myWindowManager) & " but that Window Manager is not instantiated in frame " & string(the frame))
The "5: Close My Window" handler, like its name implies, is attached to a Window Element, ie, to a sprite that has the "4: Window/Menu Element" behavior attached to it. Window Elements know the spriteNum and name of their own Manager. Elements know what the spriteNum and name of their own Manager is. They store the Manager's spriteNum in pWFSManagerSpriteNum. They store the Manager's name in pWFSManagerName. So, in the above code, the line
retrieves the Manager spriteNum from the "4: Window/Menu Element" behavior attached to the same sprite the "5: Close My Window" behavior is attached to. Now "5: Close My Window" knows the spriteNum of its own Manager. But is the Manager currently instantiated? Before the call to wfsCloseWindow, it executes
which is a handler stored in the "1: prepareMovie" script, to retrieve an integer value that is positive if the Manager is instantiated. If the Manager is instantiated, "5: Close My Window" closes its own window with the line
sendSprite (myWindowManager, #wfsCloseWindow)
The following line would have accomplished exactly the same thing:
In the above, we see how a Window Element can call a public handler stored in a Manager. In the above example, the Window Element calls a public handler stored in its own Manager sprite. Now let's look at an example in which a sprite calls a public handler stored in a Manager, but not its own Manager.
If you look at the source code for the "7: Open A Window" behavior, in the "wfsDoMeNow" handler, you see it makes a call to wfsOpenWindow.
on wfsDoMeNow me
--This behavior first checks to see if the manager of the multi-sprite to be opened
--is instantiated. If it is, then it moves the multi-sprite (or not) to where it's supposed
--to be opened. Then it opens the multi-sprite.
if theWindowManagerSpritenum = 0 then
--Then the multi-sprite you want to open is not instantiated.
alert("The '7: Open a Window' behavior on sprite " & string(spritenum) & " wants to open an uninstantiated multi-sprite. Please correct this.")
case pWFSOpeningLocation of
"Do not move it, just open it.":
sendSprite (theWindowManagerSpritenum, #wfsCenterMultiSprite)
"Open it where mouse is.":
sendSprite (theWindowManagerSpritenum, #wfsMoveFamilyTo, the mouseLoc)
"Open at absolute location.":
sendSprite (theWindowManagerSpritenum, #wfsMoveFamilyTo, point(pWFSAbsoluteLocH, pWFSAbsoluteLocV))
alert("Use the Property Inspector on sprite " & string(spritenum) & " concerning its '7: Open a Window' behavior. Look at the parameter that asks you to specify where you want the multi-sprite opened. Configure that parameter.")
sendSprite (theWindowManagerSpritenum, #wfsOpenWindow)
The "7: Open A Window" behavior knows the name of the Manager of the window it is supposed to open, since you told the behavior what window to open when you dropped the "7: Open a Window" behavior on a sprite that functions as a button to open a window. The name of the window is stored in its pWFSNameOfManagerToOpen property. But it does not know the spriteNum of the window it is to open, since the sprite is not part of the window itself, and it needs that spriteNum to make the call to wfsOpenWindow. So the "7: Open A Window" behavior contains the following line:
The wfsManagerIsInstantiated handler is stored in the "1: prepareMovie" script. Now the "7: Open A Window" behavior knows the Window Manager's spriteNum that it's supposed to open. wfsManagerIsInstantiated returns 0 if the Manager is not instantiated and returns the Manager's spriteNum if the Manager is instantiated. Somewhat subsequently, it calls wfsOpenWindow with the following line of code:
sendSprite (theWindowManagerSpritenum, #wfsOpenWindow)
The following line would have worked just as well:
Before it opens the window, it decides whether to call other public handlers stored in the Manager that position the window according to how you configured the "7: Open A Window" behavior when you dropped it on its sprite.
In the above, we see examples of how to call public handlers stored in Managers. We note that the code also uses public handlers defined in the "1: prepareMovie" script. Those handlers provide an API into gWFSMultiSpriteList, which is a global variable that stores a list of currently instantiated Managers' spritenums and names.
|3: Window Manager|