GEOS SDK TechDocs
|
|
5.4 TicTacBoard Specifics
TicTacPieceClass
contains most of the game's functionality. Since the user interacts directly with each game piece, the piece must know not only how to draw itself but also how to react to user input.
MSG_PIECE_NEW_GAME
MSG_VIS_DRAW
later and will invalidate the entire board.
MSG_VIS_DRAW
VisClass
object inherently knows its location and bounds, the object already knows where and how big the shape should appear. Whether a gray box or circle is drawn depends on the
TTP_pieceType
instance data field.
MSG_META_START_SELECT
VisContentClass
method handle it) passes it to the proper game piece object (if any) under the pointer. The game piece responds by grabbing the mouse and all subsequent pointer events.
MSG_META_DRAG_SELECT
MSG_META_START_SELECT
, indicates that the user has clicked a mouse button and has initiated a drag event. (Normally, this is used to select ranges or groups of objects; in TicTac, however, it is treated like
MSG_META_START_SELECT
.)
MSG_META_DRAG
MSG_META_PTR
MSG_META_START_SELECT
, the pointer event is sent whenever the mouse pointer is moved.) The piece determines whether or not it is being dragged around the screen. This is known as a "drag event," and the game piece responds by drawing a piece-shaped outline around the mouse pointer. This outline will follow the pointer around the screen until the user releases the mouse button (causing a
MSG_META_END_SELECT
, below). The outline is first drawn in either the
MSG_META_START_SELECT
or
MSG_META_DRAG_SELECT
handler (whichever is called to start the drag event).
MSG_META_PTR
and
MSG_META_END_SELECT
erase the outline before drawing a new one. The game piece will maintain three locations in its instance data: Its
VI_bounds
field maintains its position when selected. Its
TTP_orig(horiz/vert)Pos
fields maintain its original position when the game was first started. Its
TTP_(horiz/vert)Pos
fields maintain the current position of the outline and where the object would relocate to if the move was ended now. If the event is not a drag, the object will not react because it is assumed that no mouse button has been pressed and therefore the user is taking no action.
MSG_META_END_SELECT
MSG_VIS_DRAW
. If the new location is not legally on the game board, then the object will reset all its instance data and erase any leftover outlines, causing it to revert to its location before the select-and-drag sequence began.Each of the methods for the above messages is shown in Methods for TicTacPieceClass .
Code Display 6-6 Methods for TicTacPieceClass
/*********************************************************************** * * MESSAGE: MSG_PIECE_NEW_GAME for TicTacPieceClass * * DESCRIPTION: This message causes the piece to replace itself * to its original position. It is invoked when the * user presses the New Game trigger; the trigger sends * MSG_TICTAC_NEW_GAME to the TicTacBoard object, and * the board object sends this message to each of * the game piece objects. * * PARAMETERS: * void () ***********************************************************************/
@method TicTacPieceClass, MSG_PIECE_NEW_GAME {
/* Set the current (motion) positions to the original positions. */
pself->TTP_vertPos = pself->TTP_origVertPos;
pself->TTP_horizPos = pself->TTP_origHorizPos;
/* Send a MSG_VIS_BOUNDS_CHANGED to ourselves to make * sure the old bounds get redrawn. This message will * cause an invalidation of the document where the old * (passed) bounds were, causing that portion of the * window to be redrawn. */
@call self::MSG_VIS_BOUNDS_CHANGED(pself->VI_bounds.R_bottom, pself->VI_bounds.R_right, pself->VI_bounds.R_top, pself->VI_bounds.R_left);
/* Set the bounds of the object (VI_bounds) back to * their original values. The Rectangle structure * contains four fields, each of which must be set. */
pself->VI_bounds.R_left = pself->TTP_origHorizPos;
pself->VI_bounds.R_top = pself->TTP_origVertPos;
pself->VI_bounds.R_right = (pself->TTP_origHorizPos + PIECE_WIDTH);
pself->VI_bounds.R_bottom = (pself->TTP_origVertPos + PIECE_HEIGHT);
/* This method does not need to invoke a MSG_VIS_DRAW * because the TicTacBoard object will do that. The * piece object will later receive a MSG_VIS_DRAW that * will cause the piece to be redrawn back at its * original location (the newly set bounds). */ }
/*********************************************************************** * * MESSAGE: MSG_VIS_DRAW for TicTacPieceClass * * DESCRIPTION: Draw the piece at the current location. If the piece * is a "box," draw a gray square. If the piece is a * "ring," draw a gray circle. This message is received * whenever a portion of the view window becomes invalid; * TicTacView will send a MSG_META_EXPOSED to TicTacBoard, * which will send itself (by default) a MSG_VIS_DRAW. * The MSG_VIS_DRAW will be handled and then will be * passed on to each of the game pieces. Then each piece * (in this handler) will draw itself at its own bounds. * * PARAMETERS: * void (word drawFlags GStateHandle gstate) * ***********************************************************************/
@method TicTacPieceClass, MSG_VIS_DRAW {
/* Set the mode to MM_COPY; this means that the image
* drawn now will be drawn over whatever is there now.*/
GrSetMixMode(gstate, MM_COPY);
/* If the type is TTPT_BOX, set the color to gray and * draw a rectangle the size of the object's bounds. * Otherwise (since there are just two types), set the * color to gray and draw an ellipse of that size. */
if (pself->TTP_pieceType == TTPT_BOX) {
GrSetAreaColor(gstate, CF_INDEX, C_DARK_GRAY, 0, 0);
GrFillRect(gstate, pself->VI_bounds.R_left, pself->VI_bounds.R_top,
pself->VI_bounds.R_right, pself->VI_bounds.R_bottom);
} else {
GrSetAreaColor(gstate, CF_INDEX, C_LIGHT_GRAY, 0, 0);
GrFillEllipse(gstate, pself->VI_bounds.R_left, pself->VI_bounds.R_top,
pself->VI_bounds.R_right, pself->VI_bounds.R_bottom);
}
/* After handling the message, call the superclass to
* ensure that no default behavior has been mucked up.
* This is actually not necessary in this particular case. */
@callsuper();
}
/*********************************************************************** * MESSAGE: MSG_META_START_SELECT for TicTacPieceClass * * DESCRIPTION: Grabs the mouse and calls for future pointer events. * When the user clicks in the view, TicTacView will pass * the click event to TicTacBoard. Since TicTacBoardClass * does not intercept the event, VisContentClass passes * it on to its child object currently under the pointer. * * When the piece object receives this message, it means * it has been clicked on by the user and the mouse button * is still down. The piece must grab the mouse so that it * gets all future mouse events, and it must request that * all future mouse events be sent to it. This ensures * that if the pointer leaves the object's bounds while * the button is still pressed, the piece object will still * receive all the pointer events (otherwise they would be * sent to whatever object was under the new pointer * position). * PARAMETERS: * void (MouseReturnParams *retVal, word xPosition, * word yPosition, word inputState) ***********************************************************************/
@method TicTacPieceClass, MSG_META_START_SELECT {
/* First grab the gadget exclusive so we're allowed to * grab the mouse. Then grab the mouse, so all future * pointer events get passed directly to the game piece. */
@call @visParent::MSG_VIS_TAKE_GADGET_EXCL(oself);
@call self::MSG_VIS_GRAB_MOUSE(); /* grab mouse */
/* Finally, return that this particular click * event has been processed. If this flag is * not returned, the system will send out the * click event again. */
retVal->flags = MRF_PROCESSED; /* this event processed */ }
/*********************************************************************** * * MESSAGE: MSG_META_DRAG_SELECT for TicTacPieceClass * * DESCRIPTION: This message is sent to the piece object when the * select button has been pressed and the mouse has been * moved, resulting in a "drag-select" event. * For event processing from the View, see the header * for MSG_META_START_SELECT. * * PARAMETERS: * void (MouseReturnParams *retVal, word xPosition, * word yPosition, word inputState) ***********************************************************************/
@method TicTacPieceClass, MSG_META_DRAG_SELECT {
GStateHandle gstate; /* temporary gstate to draw to */
WindowHandle win; /* window handle of view window */
/* Start off by setting the flag indicating that
* the piece is being dragged around the screen. */
pself->TTP_dragging = TRUE;
/* Next, get the window handle of the view window. * Then, create a new, temporary gstate to draw into * for that window. */
win = @call TicTacView::MSG_GEN_VIEW_GET_WINDOW();
gstate = GrCreateState(win);
/* Now, set the current position of the game piece * to be centered on the pointer. */
pself->TTP_vertPos = yPosition - (PIECE_HEIGHT/2);
pself->TTP_horizPos = xPosition - (PIECE_WIDTH/2);
/* Now, set the drawing mode of the game piece * to MM_INVERT to draw a new game piece outline. * MM_INVERT is chosen so the outline can be redrawn * in invert mode later to erase it and not destroy * anything under it. */
GrSetMixMode(gstate, MM_INVERT);
/* Now draw the outline. If the game piece is of type * TTPT_BOX, draw a rectangle outline. Otherwise, draw * an ellipse outline. */
if (pself->TTP_pieceType == TTPT_BOX) {
GrDrawRect(gstate, pself->TTP_horizPos, pself->TTP_vertPos,
(pself->TTP_horizPos + PIECE_WIDTH),
(pself->TTP_vertPos + PIECE_HEIGHT));
} else {
GrDrawEllipse(gstate, pself->TTP_horizPos, pself->TTP_vertPos,
(pself->TTP_horizPos + PIECE_WIDTH),
(pself->TTP_vertPos + PIECE_HEIGHT));
}
/* Next, destroy the temporary gstate. This is important * to make sure the gstate does not stay in memory and * begin to slow down the system as more and more * temporary gstates are created but not destroyed. */
GrDestroyState(gstate);
/* Finally, return that this event has been processed * by this method. */
retVal->flags = MRF_PROCESSED; }
/*********************************************************************** * * MESSAGE: MSG_META_PTR for TicTacPieceClass * * DESCRIPTION: This message is received whenever the pointer passes * over this game piece object's bounds (and another * game piece is not sitting directly on top of it). * See MSG_META_START_SELECT for a description of how the event * gets passed from TicTacView to this object. * * This message can be either a drag event or a simple * pointer event. If the latter, we want to do nothing * because no mouse button is pressed. If the latter, * we want to execute the same function as MSG_META_DRAG. * * PARAMETERS: * void (MouseReturnParams *retVal, word xPosition, * word yPosition, word inputState) * ***********************************************************************/
@method TicTacPieceClass, MSG_META_PTR {
GStateHandle gstate; /* temporary gstate to draw to */
WindowHandle win; /* window handle of view window */
/* First check if this is a drag event. If not, do * nothing. If so, then draw a new outline and erase * the old outline. */
if (pself->TTP_dragging) {
/* Get the view's window handle and create a * temporary gstate for drawing into. */
win = @call TicTacView::MSG_GEN_VIEW_GET_WINDOW(); gstate = GrCreateState(win);
/* Set the drawing mode of the game piece to * MM_INVERT for outline drawing. */
GrSetMixMode(gstate, MM_INVERT);
/* Erase the old outline by drawing an inverse * outline at the old bounds. */
if (pself->TTP_pieceType == TTPT_BOX) {
GrDrawRect(gstate, pself->TTP_horizPos, pself->TTP_vertPos,
(pself->TTP_horizPos + PIECE_WIDTH),
(pself->TTP_vertPos + PIECE_HEIGHT));
} else {
GrDrawEllipse(gstate, pself->TTP_horizPos, pself->TTP_vertPos,
(pself->TTP_horizPos + PIECE_WIDTH),
(pself->TTP_vertPos + PIECE_HEIGHT));
}
/* Now set the current motion position to be * centered on the pointer. */
pself->TTP_vertPos = yPosition - (PIECE_HEIGHT/2); pself->TTP_horizPos = xPosition - (PIECE_WIDTH/2);
/* Draw the new outline at the current position.*/
if (pself->TTP_pieceType == TTPT_BOX) {
GrDrawRect(gstate, pself->TTP_horizPos, pself->TTP_vertPos,
(pself->TTP_horizPos + PIECE_WIDTH),
(pself->TTP_vertPos + PIECE_HEIGHT));
} else {
GrDrawEllipse(gstate, pself->TTP_horizPos, pself->TTP_vertPos,
(pself->TTP_horizPos + PIECE_WIDTH),
(pself->TTP_vertPos + PIECE_HEIGHT));
}
/* Destroy the temporary gstate and return that * this event has been processed. */
GrDestroyState(gstate);
}
retVal->flags = MRF_PROCESSED;
}
/*********************************************************************** * * MESSAGE: MSG_META_END_SELECT for TicTacPieceClass * * DESCRIPTION: This message is received when the selection button has * been released and this game piece had the mouse grab. * All it does is release the gadget exclusive, which will * cause us to end any dragging in progress and release * the mouse. * When we release the gadget exclusive, the UI will then * sent MSG_VIS_LOST_GADGET_EXCL to this piece, which will * tell us to erase the outline and draw the game piece. * PARAMETERS: * void (MouseReturnParams *retVal, word xPosition, * word yPosition, word inputState); ***********************************************************************/
@method TicTacPieceClass, MSG_META_END_SELECT {
/* Release the gadget exclusive, then return that the
* event has been processed. */
@call @visParent::MSG_VIS_RELEASE_GADGET_EXCL(oself);
retVal->flags = MRF_PROCESSED; /* this event processed */
}
/*********************************************************************** * * MESSAGE: MSG_VIS_LOST_GADGET_EXCL for TicTacPieceClass * * DESCRIPTION: This message is received when the piece lots go of the * gadget exclusive (see MSG_META_END_SELECT, above). * It first checks to see if the new, proposed bounds are * on the game board. If the bounds are valid, then * it sets the objects VI_bounds field to the new values * and causes the object to erase its original drawing * and draw itself at its new bounds. If the bounds are * not on the game board, it will retain the original bounds * and redraw using them. * * PARAMETERS: * void () * ***********************************************************************/
@method TicTacPieceClass, MSG_VIS_LOST_GADGET_EXCL {
WindowHandle win; /* window handle of view window */
GStateHandle gstate; /* temporary gstate to draw to */
/* First check if the piece was being dragged.
* If not, we don't have to do anything. */
if (pself->TTP_dragging) {
/* Get the window handle of the view window and * create a temporary gstate for it to draw to. */
win = @call TicTacView::MSG_GEN_VIEW_GET_WINDOW(); gstate = GrCreateState(win);
/* Set the mode for drawing the outline. */
GrSetMixMode(gstate, MM_INVERT);
/* If the game piece type is TTPT_BOX, draw a rectangle * outline. Otherwise draw an ellipse outline. */
if (pself->TTP_pieceType == TTPT_BOX) {
GrDrawRect(gstate, pself->TTP_horizPos, pself->TTP_vertPos,
((pself->TTP_horizPos) + PIECE_WIDTH),
((pself->TTP_vertPos) + PIECE_HEIGHT));
} else {
GrDrawEllipse(gstate, pself->TTP_horizPos, pself->TTP_vertPos,
((pself->TTP_horizPos) + PIECE_WIDTH),
((pself->TTP_vertPos) + PIECE_HEIGHT));
}
/* Check to see if the new bounds are on the game * board. If they are, set the object's bounds to the * new values. If they are not, retain the original * values and set the values to those last stored. */
if (@call TicTacBoard::MSG_TICTAC_VALIDATE_BOUNDS(
((pself->TTP_vertPos) + PIECE_HEIGHT),
((pself->TTP_horizPos) + PIECE_WIDTH),
pself->TTP_vertPos,
pself->TTP_horizPos)) {
/* Invalidate the original drawing of the game piece. * Send the VI_bounds rectangle as the parameters * because they have not been changed since the * START_SELECT. This message is the equivalent of * calling GrInvalRect() with the same bounds. */
@call self::MSG_VIS_BOUNDS_CHANGED(pself->VI_bounds.R_bottom, pself->VI_bounds.R_right, pself->VI_bounds.R_top, pself->VI_bounds.R_left);
/* Now set the current position to be centered * on the pointer image. */
pself->TTP_vertPos = yPosition - (PIECE_HEIGHT/2); pself->TTP_horizPos = xPosition - (PIECE_WIDTH/2);
/* Set the game piece object's bounds to * the new coordinates. */
pself->VI_bounds.R_left = pself->TTP_horizPos; pself->VI_bounds.R_right = (pself->TTP_horizPos) + PIECE_WIDTH; pself->VI_bounds.R_top = pself->TTP_vertPos; pself->VI_bounds.R_bottom = (pself->TTP_vertPos) + PIECE_HEIGHT;
} else {
/* If the bounds are not on the game board, then reset * the current positions to be the original bounds. */
pself->TTP_horizPos = pself->VI_bounds.R_left; pself->TTP_vertPos = pself->VI_bounds.R_top; }
/* Now, the game piece must draw itself at its newly- * set bounds (will draw itself over its original * picture if the new bounds were invalid). */
@call self::MSG_VIS_DRAW(0, gstate);
/* Destroy the temporary gstate used for drawing. */
GrDestroyState(gstate);
/* Finally, clear the dragging flag to indicate that * no drag event is in progress. */
pself->TTP_dragging = FALSE;
}
/* Release the mouse grab now that the move has * finished. Other objects in the view (other game * pieces, for example) may now receive pointer, * select, and drag events. */
@call self::MSG_VIS_RELEASE_MOUSE();
}
GEOS SDK TechDocs
|
|
5.4 TicTacBoard Specifics