This is EKInfoManager.m in view mode; [Download] [Up]
/* * EKInfoManager * description: a generic object for having a spiffy info panel * history: * 5/10/93 [Erik Kay] - created * 5/20/93 [Erik Kay] - dissolve from author tiff to app tiff * 5/23/93 [Erik Kay] - added other animation effects: * random, checker, circle, slide * bugs: * this should be able to deal with multiple authors * todo: * could always add a few more animation effects: * barn door, 4 triangles, rectangle, etc. */ #import "EKInfoManager.h" //! <<<WARNING>>> //! THIS VARIABLE MUST BE DEFINED ELSEWHERE IN THE APP! //! define it to be any string you want, but it must be defined //! I've found that vers_string works well here. - EK extern char VERS_NUM[]; // timed entry function for animating to author TIFF void animateToAuthor (DPSTimedEntry te, double now, void *data) { static int count = 0; [(id)data animateToAuthor:++count]; if (count > [(id)data animationSteps]) { count = 0; [(id)data finishAnimation]; } } // timed entry function for animating to app TIFF void animateToApp (DPSTimedEntry te, double now, void *data) { static int count = 0; [(id)data animateToApp:++count]; if (count > [(id)data animationSteps]) { count = 0; [(id)data finishAnimation]; } } @implementation EKInfoManager - init { [super init]; // you've got to have an image named "author.tiff" in your app wrapper authorTIFF = [NXImage findImageNamed:"author"]; //! you don't need an image named "app.tiff", this will just grab the //! icon you're using for your app tile appTIFF = [NXImage findImageNamed:"app"]; te = NULL; return self; } // load the info panel from the nib if not loaded already - loadInfoPanel:sender { NXSize sz; if (!infoPanel) { // load the nib [NXApp loadNibSection:"Info.nib" owner:self withNames:NO]; // initialize the images [authorTIFF getSize:&sz]; workTIFF = [[NXImage alloc] initSize:&sz]; // display the version string in the version text field [versionNumber setStringValue:VERS_NUM]; } return self; } // show the info panel - showInfoPanel:sender { NXPoint pt = {0,0}; // load it if not loaded already [self loadInfoPanel:sender]; // the button starts out as the app icon currentImage = appTIFF; // draw it into the work area [workTIFF lockFocus]; [currentImage composite:NX_SOVER toPoint:&pt]; [workTIFF unlockFocus]; // put it into the button [iconButton setImage:workTIFF]; // bring the panel up [infoPanel makeKeyAndOrderFront:sender]; return self; } // In your info panel, put a transparent button behind the author's name // then connect the button to this method // This action changes the image to the author if it's not at the author // already. - switchToAuthor:sender { if (!te && (currentImage != authorTIFF)) { te = DPSAddTimedEntry(0.1,animateToAuthor, (void *)self,NX_BASETHRESHOLD); currentImage = authorTIFF; } return self; } // In your info panel, put a transparent button behind your app's name // then connect the button to this method // This action changes the image to the app if it's not at the app // already. - switchToApp:sender { if (!te && (currentImage != appTIFF)) { te = DPSAddTimedEntry(0.1,animateToApp,(void *)self,NX_BASETHRESHOLD); currentImage = appTIFF; } return self; } // In the info panel, hook up the button that the image is being drawn into // to this action. It will toggle the image back and forth. - toggleButton:sender { if (currentImage == appTIFF) { [self switchToAuthor:sender]; } else { [self switchToApp:sender]; } return self; } // Actually do the animation to the author tiff. // The example implementation here just does a dissolve to the author tiff. // This is a prime candidate for subclassing. If you want to use a different // effect, just subclass away. - animateToAuthor:(int)i { [self dissolveTo:authorTIFF step:i]; return self; } // Actually do the animation to the app tiff. // The example implementation here just does a dissolve to the app tiff. // This is a prime candidate for subclassing. If you want to use a different // effect, just subclass away. - animateToApp:(int)i { [self dissolveTo:appTIFF step:i]; return self; } // how many steps in the animation? -(int)animationSteps { return animationSteps; } - setAnimationSteps:(int)steps { animationSteps = steps; return self; } // clean up - finishAnimation { DPSRemoveTimedEntry(te); te = NULL; return self; } // // here are all of the various animation effects // // do dissolve animation: very simple stuff - dissolveTo:image step:(int)i { NXPoint pt = {0.0,0.0}; float dissolve; dissolve = (float)i/(float)animationSteps; [workTIFF lockFocus]; [image dissolve:dissolve toPoint:&pt]; [workTIFF unlockFocus]; [iconButton setImage:workTIFF]; [iconButton display]; [infoPanel flushWindow]; return self; } #define LEFT 0 #define RIGHT 1 #define BOTTOM 2 #define UP 3 // slide an image in from a cycling direction - slideTo:image step:(int)i { static int direction = -1; if (i == 1) { direction++; if (direction > UP) direction = LEFT; } [self slideTo:image step:i from:direction]; return self; } // slide an image in from a specified direction - slideTo:image step:(int)i from:(int)direction { NXRect r; NXSize s; int width, height; NXPoint pt = {0.0,0.0}; [image getSize:&s]; width = i * (s.width / animationSteps); height = i * (s.height / animationSteps); switch (direction) { case LEFT: NXSetRect(&r,s.width - width,0,width,s.height); break; case RIGHT: NXSetRect(&r,0,0,width,s.height); pt.x = s.width - width; break; case BOTTOM: NXSetRect(&r,0,s.height - height,s.width,height); break; case UP: NXSetRect(&r,0,0,s.width,height); pt.y = s.height - height; break; default: break; } [workTIFF lockFocus]; [image composite:NX_COPY fromRect:&r toPoint:&pt]; [workTIFF unlockFocus]; [iconButton setImage:workTIFF]; [iconButton display]; [infoPanel flushWindow]; return self; } // For all of the effects after this, they all use some basic compositing // compositing tricks. Using this technique, you can pretty much do any // simple animation from one tiff to another with very little work. // Here are the basic steps: // (1) create a new image of the same size of the target image // (2) in a completely opaque color, draw the pattern that defines your // effect on to the image (all of the real work is here) // (3) do an NX_SIN composite of the target image on to the pattern image // (4) NX_COPY the original image (the one being drawn over, not the // image being animated to) on to the workTIFF // (5) NX_SOVER the pattern image on to workTIFF // (6) draw the workTIFF on to the button // when commenting these functions, I'll just say which step they are // do the random fill animation - randomTo:image step:(int)i { id img; NXSize imgSize; NXRect r, *rects; NXPoint pt = {0.0,0.0}; int num, x, y, index, count; float percent; // step (1) [image getSize:&imgSize]; img = [[NXImage alloc] initSize:&imgSize]; // step (2) - this is pretty slow. basically I'm drawing a random series // of 1x1 rectangles on the image. This gives the random // "beam me up" effect [img lockFocus]; PSsetgray(0.0); PSsetalpha(0.0); NXSetRect(&r,0,0,imgSize.width,imgSize.height); NXRectFill(&r); PSsetalpha(1.0); count = imgSize.width * imgSize.height; percent = i / (float)animationSteps; if (i <= animationSteps) { rects = malloc(sizeof(NXRect) * count); for (x = 0; x < imgSize.width; x++) { for (y = 0; y < imgSize.height; y++) { index = x + y * imgSize.width; num = random() & 63; //! this is kind of dumb, vaguely size //! dependant stuff here. if (num <= (63 * percent)) NXSetRect(&rects[index],x,y,1,1); else NXSetRect(&rects[index],0,0,0,0); } } NXRectFillList(rects,count); free(rects); } else { NXRectFill(&r); } // step (3) [image composite:NX_SIN toPoint:&pt]; [img unlockFocus]; // step (4) [workTIFF lockFocus]; if (image == authorTIFF) [appTIFF composite:NX_COPY toPoint:&pt]; else [authorTIFF composite:NX_COPY toPoint:&pt]; // step (5) [img composite:NX_SOVER toPoint:&pt]; [workTIFF unlockFocus]; // step (6) [iconButton setImage:workTIFF]; [iconButton display]; [infoPanel flushWindow]; [img free]; return self; } // animate in a kind of 'checker board' style - checkerTo:image step:(int)i { NXRect rects[64], r; NXSize imgSize,rectSize; NXPoint pt = {0.0,0.0}; float height,h; int x, y, index; id img; // step (1) [image getSize:&imgSize]; img = [[NXImage alloc] initSize:&imgSize]; // step (2) - we're building up a checkerboard of rectangles. First the // "black" squares, then the "red" squares. rectSize.width = imgSize.width / 8; rectSize.height = imgSize.height / 8; height = 2 * i * (rectSize.height / animationSteps); h = height - rectSize.height; for (x = 0; x < 8; x ++) { for (y = 0; y < 8; y ++) { index = x + y * 8; if (height > rectSize.height) { if ((x % 2) == (y % 2)) { NXSetRect(&rects[index],x * rectSize.width, (y + 1) * rectSize.height - h, rectSize.width, h); } else { NXSetRect(&rects[index],x * rectSize.width, y * rectSize.height, rectSize.width,rectSize.height); } } else { if ((x % 2) != (y % 2)) { NXSetRect(&rects[index],x * rectSize.width, y * rectSize.height,rectSize.width, height); } else { NXSetRect(&rects[index],0,0,0,0); } } } } [img lockFocus]; PSsetgray(0.0); PSsetalpha(0.0); NXSetRect(&r,0,0,imgSize.width,imgSize.height); NXRectFill(&r); PSsetalpha(1.0); // here's where step (2) actually happens. All of the previous was just // setup. :-) NXRectFillList(rects,64); // step (3) [image composite:NX_SIN toPoint:&pt]; [img unlockFocus]; // step (4) [workTIFF lockFocus]; if (image == authorTIFF) [appTIFF composite:NX_COPY toPoint:&pt]; else [authorTIFF composite:NX_COPY toPoint:&pt]; // step (5) [img composite:NX_SOVER toPoint:&pt]; [workTIFF unlockFocus]; // step (6) [iconButton setImage:workTIFF]; [iconButton display]; [infoPanel flushWindow]; [img free]; return self; } // animate as a growing circle in the center - circleTo:image step:(int)i { NXSize sz; NXRect r; NXPoint center, pt = {0.0,0.0}; float radius; id img; // step (1) [image getSize:&sz]; img = [[NXImage alloc] initSize:&sz]; // step (2) - just draw a circle [img lockFocus]; PSsetgray(0.0); PSsetalpha(0.0); NXSetRect(&r,0,0,sz.width,sz.height); NXRectFill(&r); PSsetalpha(1.0); center.x = sz.width / 2; center.y = sz.height / 2; radius = sqrt(center.x * center.x + center.y * center.y); radius = i * (radius / animationSteps); PSarc(center.x,center.y,radius,0,360); PSfill(); // step (3) [image composite:NX_SIN toPoint:&pt]; [img unlockFocus]; // step (4) [workTIFF lockFocus]; if (image == authorTIFF) [appTIFF composite:NX_COPY toPoint:&pt]; else [authorTIFF composite:NX_COPY toPoint:&pt]; // step (5) [img composite:NX_SOVER toPoint:&pt]; [workTIFF unlockFocus]; // step (6) [iconButton setImage:workTIFF]; [iconButton display]; [infoPanel flushWindow]; [img free]; return self; } @end
These are the contents of the former NiCE NeXT User Group NeXTSTEP/OpenStep software archive, currently hosted by Netfuture.ch.