Retired Document
Important: This sample code may not represent best practices for current development. The project may use deprecated symbols and illustrate technologies and techniques that are no longer recommended.
StarMenu.cpp
/* |
* File: StarMenu.cpp of StarMenu |
* |
* Contains: This file contains the implementation for a custom subclass (in the |
* HIObject sense) of kHIMenuViewClassID. The subclass draws a lovely |
* star shaped menu and allows the user to select a color from it. |
* |
* This file also contains implementations of the utility routines for |
* changing the items in the menu as declared in StarMenu.h |
* |
* Of particular interest is the handling of the kEventMenuCreateFrameView |
* event which creates a custom frame view for our star-shaped menu. That way |
* we don't end up with a stellar menu in a square frame. |
* |
* Version: 1.0.1 |
* |
* Disclaimer: IMPORTANT: This Apple software is supplied to you by Apple Computer, Inc. |
* ("Apple") in consideration of your agreement to the following terms, and your |
* use, installation, modification or redistribution of this Apple software |
* constitutes acceptance of these terms. If you do not agree with these terms, |
* please do not use, install, modify or redistribute this Apple software. |
* |
* In consideration of your agreement to abide by the following terms, and subject |
* to these terms, Apple grants you a personal, non-exclusive license, under AppleÕs |
* copyrights in this original Apple software (the "Apple Software"), to use, |
* reproduce, modify and redistribute the Apple Software, with or without |
* modifications, in source and/or binary forms; provided that if you redistribute |
* the Apple Software in its entirety and without modifications, you must retain |
* this notice and the following text and disclaimers in all such redistributions of |
* the Apple Software. Neither the name, trademarks, service marks or logos of |
* Apple Computer, Inc. may be used to endorse or promote products derived from the |
* Apple Software without specific prior written permission from Apple. Except as |
* expressly stated in this notice, no other rights or licenses, express or implied, |
* are granted by Apple herein, including but not limited to any patent rights that |
* may be infringed by your derivative works or by other works in which the Apple |
* Software may be incorporated. |
* |
* The Apple Software is provided by Apple on an "AS IS" basis. APPLE MAKES NO |
* WARRANTIES, EXPRESS OR IMPLIED, INCLUDING WITHOUT LIMITATION THE IMPLIED |
* WARRANTIES OF NON-INFRINGEMENT, MERCHANTABILITY AND FITNESS FOR A PARTICULAR |
* PURPOSE, REGARDING THE APPLE SOFTWARE OR ITS USE AND OPERATION ALONE OR IN |
* COMBINATION WITH YOUR PRODUCTS. |
* |
* IN NO EVENT SHALL APPLE BE LIABLE FOR ANY SPECIAL, INDIRECT, INCIDENTAL OR |
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE |
* GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) |
* ARISING IN ANY WAY OUT OF THE USE, REPRODUCTION, MODIFICATION AND/OR DISTRIBUTION |
* OF THE APPLE SOFTWARE, HOWEVER CAUSED AND WHETHER UNDER THEORY OF CONTRACT, TORT |
* (INCLUDING NEGLIGENCE), STRICT LIABILITY OR OTHERWISE, EVEN IF APPLE HAS BEEN |
* ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. |
* |
* Copyright: Copyright © 2005 Apple Computer, Inc, All Rights Reserved |
*/ |
#include "StarMenu.h" |
#include "StarMenuFrame.h" |
const EventParamName kEventParamStarMenuRadius = 'RAD '; |
const OSType kStarMenuCreator = 'CrcM'; |
const OSType kStarMenuColor = 'Colr'; |
// HIView instance data. |
struct StarMenuData |
{ |
MenuRef menu; |
HIViewRef hiSelf; |
EventHandlerRef rootHandler; |
float radius; |
}; |
// These are the events handled by the Menu View. |
static EventTypeSpec gStarMenuHandledEvents[] = |
{ |
{ kEventClassHIObject, kEventHIObjectConstruct } , |
{ kEventClassHIObject, kEventHIObjectInitialize }, |
{ kEventClassHIObject, kEventHIObjectDestruct }, |
{ kEventClassControl, kEventControlHitTest }, |
{ kEventClassControl, kEventControlGetPartRegion }, |
{ kEventClassControl, kEventControlDraw }, |
{ kEventClassControl, kEventControlGetOptimalBounds }, |
{ kEventClassMenu, kEventMenuCreateFrameView }, |
{ kEventClassMenu, kEventMenuBecomeScrollable }, |
{ kEventClassMenu, kEventMenuCeaseToBeScrollable }, |
// we don't know that scrollable is strictly necesssary |
// in this case but we'll add it anyway |
{ kEventClassScrollable, kEventScrollableGetInfo }, |
}; |
static HIObjectClassRef gStarMenuClassRef = NULL; |
static OSStatus StarMenuEventHandler(EventHandlerCallRef inCallRef, EventRef inEvent, void *refcon); |
static OSStatus HandleObjectEvents(EventHandlerCallRef inCallRef, EventRef inEvent, StarMenuData* data); |
static OSStatus HandleControlEvents(EventHandlerCallRef inCallRef, EventRef inEvent, StarMenuData* data); |
static OSStatus HandleMenuEvents(EventHandlerCallRef inCallRef, EventRef inEvent, StarMenuData* data); |
static OSStatus HandleScrollableEvents(EventHandlerCallRef inCallRef, EventRef event, StarMenuData* menuData ); |
static void DrawStarMenu(EventRef drawEvent, StarMenuData *menuData); |
static bool GetStarMenuPartRegion( EventRef inEvent, StarMenuData* data); |
static void HitTestStarMenu( EventRef inEvent, StarMenuData* data); |
static CGPathRef CreatePathForStarMenuItem(StarMenuData *menuData, MenuItemIndex whichItem); |
static CGPathRef CreatePathForEntireStarMenu(StarMenuData *menuData); |
/* ------------------------------------------------------- CreateStarMenu */ |
/* |
Public routine for creating an instance of our star-shaped menu |
*/ |
OSStatus CreateStarMenu(MenuID inMenuID, float radius, MenuRef *outMenu) |
{ |
EventRef initEvent = NULL; |
OSStatus errStatus = noErr; |
// If my custom menu view type is not yet registered then |
// register it as a sublcass of MenuView (kHIMenuViewClassID) |
static bool isClassRegistered = false; |
if(!isClassRegistered) { |
verify_noerr( HIObjectRegisterSubclass ( |
kStarMenuClassID, |
kHIMenuViewClassID, |
kNilOptions, |
StarMenuEventHandler, |
GetEventTypeCount( gStarMenuHandledEvents ), |
gStarMenuHandledEvents, |
NULL, |
&gStarMenuClassRef)); |
isClassRegistered = true; |
} |
// We're going to create an instance of our custom menu type. To do that we |
// need to tell the system how to create one of our menus. We do that with an |
// MenuDefSpec. |
// An HIObject based menu view needs to have an initialization event |
errStatus = CreateEvent( NULL, kEventClassHIObject, kEventHIObjectInitialize, 0, 0, &initEvent ); |
if(noErr == errStatus && initEvent) { |
MenuDefSpec customMenuDef; |
// Pass the init parameters that are specific to our class. The operating system |
// will add it's own initialization parameters in CreateCustomMenu |
SetEventParameter(initEvent, kEventParamStarMenuRadius, typeFloat, sizeof(radius), &radius); |
// setup the menu def function specification that we'll pass to CreateCustomMenu |
customMenuDef.defType = kMenuDefClassID; |
customMenuDef.u.view.classID = kStarMenuClassID; |
customMenuDef.u.view.initEvent = initEvent; |
// Finally, we can create the menu |
errStatus = CreateCustomMenu( &customMenuDef, inMenuID, 0, outMenu ); |
// We're done with our custom event |
ReleaseEvent(initEvent); |
initEvent = nil; |
} |
return errStatus; |
} |
/* ------------------------------------------------- SetStarMenuItemColor */ |
/* |
Public routine that establishes the color of a given menu item |
*/ |
OSStatus SetStarMenuItemColor( |
MenuRef starMenu, |
MenuItemIndex whichItem, |
const RGBColor newColor) |
{ |
return SetMenuItemProperty( starMenu, whichItem, kStarMenuCreator, kStarMenuColor, sizeof(RGBColor), &newColor); |
} |
/* ------------------------------------------------- GetStarMenuItemColor */ |
/* |
Public routine that returns the color of a given menu item |
*/ |
OSStatus GetStarMenuItemColor( |
MenuRef starMenu, |
MenuItemIndex whichItem, |
RGBColor *outItemColor) |
{ |
return GetMenuItemProperty(starMenu, whichItem, kStarMenuCreator, kStarMenuColor, sizeof(RGBColor), NULL, outItemColor); |
} |
/* ------------------------------------------------- StarMenuEventHandler */ |
/* |
Main event handler that defines the behavior of the star menu view. |
This is little more than a dispatcher to more specialized handlers |
*/ |
OSStatus StarMenuEventHandler(EventHandlerCallRef inCallRef, EventRef inEvent, void *inUserData) |
{ |
OSStatus retVal = eventNotHandledErr; |
switch(GetEventClass(inEvent)) { |
case kEventClassHIObject : |
retVal = HandleObjectEvents(inCallRef, inEvent, (StarMenuData *) inUserData ); |
break; |
case kEventClassControl : |
retVal = HandleControlEvents(inCallRef, inEvent, (StarMenuData *) inUserData ); |
break; |
case kEventClassMenu : |
retVal = HandleMenuEvents(inCallRef, inEvent, (StarMenuData *) inUserData); |
break; |
case kEventClassScrollable : |
retVal = HandleScrollableEvents(inCallRef, inEvent, (StarMenuData *) inUserData); |
break; |
} |
return retVal; |
} |
/* ------------------------------------------------------ HandleControlEvents */ |
/* |
Handle controls of kEventClassControl that are passed to our Menu view |
*/ |
OSStatus HandleControlEvents(EventHandlerCallRef inCallRef, EventRef inEvent, StarMenuData* data) |
{ |
OSStatus retVal = eventNotHandledErr; |
switch(GetEventKind(inEvent)) { |
case kEventControlInitialize : |
retVal = CallNextEventHandler( inCallRef, inEvent ); |
break; |
case kEventControlHitTest : |
HitTestStarMenu(inEvent, data); |
retVal = noErr; |
break; |
case kEventControlGetPartRegion : |
if(GetStarMenuPartRegion(inEvent, data)) { |
retVal = noErr; |
} |
break; |
case kEventControlDraw : |
DrawStarMenu(inEvent, data); |
retVal = noErr; |
break; |
case kEventControlGetOptimalBounds : { |
HIRect bounds; |
bounds.origin.x = bounds.origin.y = 0; |
bounds.size.width = bounds.size.height = 2 * data->radius; |
SetEventParameter(inEvent, kEventParamControlOptimalBounds, typeHIRect, sizeof( bounds ), &bounds ); |
retVal = noErr; |
} break; |
default: |
break; |
} |
return retVal; |
} |
/* ------------------------------------------------------- HandleObjectEvents */ |
/* |
Handle the Carbon Event messages necessary to implement a custom subclass |
of some HIObject-based class. |
<file:///Developer/Documentation/Carbon/Reference/HIObjectReference/index.html> |
*/ |
OSStatus HandleObjectEvents( |
EventHandlerCallRef inCallRef, |
EventRef inEvent, |
StarMenuData* menuData) |
{ |
OSStatus err = eventNotHandledErr; |
switch ( GetEventKind( inEvent ) ) { |
case kEventHIObjectConstruct: |
menuData = (StarMenuData *) calloc(1, sizeof(StarMenuData)); |
menuData->menu = NULL; |
// save off a pointer to myself as an HIViewRef |
GetEventParameter(inEvent, kEventParamHIObjectInstance, typeHIObjectRef, NULL, sizeof( menuData->hiSelf ), NULL, &menuData->hiSelf ); |
// This important step actually associates our data with the HIObject that is being created |
SetEventParameter(inEvent, kEventParamHIObjectInstance, typeVoidPtr, sizeof( menuData ), &menuData ); |
err = noErr; |
break; |
case kEventHIObjectInitialize: |
// extract the parameters passed in the initialization events and |
// save them into our instance data. |
err = CallNextEventHandler( inCallRef, inEvent ); |
if ( err == noErr ) { |
// The kEventParamMenuRef is placed in the initialization event by the operating system |
// when creating an instance of kHIMenuClassID. |
GetEventParameter(inEvent, kEventParamMenuRef, typeMenuRef, NULL, sizeof(menuData->menu), NULL, &menuData->menu); |
// This is the parameter that we added to the event in CreateStarMenu |
GetEventParameter(inEvent, kEventParamStarMenuRadius, typeFloat, NULL, sizeof(menuData->radius), NULL, &menuData->radius); |
} |
break; |
case kEventHIObjectDestruct: |
free( (void*) menuData ); |
err = noErr; |
break; |
/* No need to override the "kEventHIObjectIsEqual" event... the default behavior should work fine */ |
/* We also ignore the "kEventHIObjectPrintDebugInfo" event... we're evil!" */ |
default: |
break; |
} |
return err; |
} |
/* --------------------------------------------------------- HandleMenuEvents */ |
/* |
Handle the kEventMenuCreateFrameView event to create a custom |
menu frame. |
*/ |
OSStatus HandleMenuEvents( |
EventHandlerCallRef inCallRef, |
EventRef inEvent, |
StarMenuData* data) |
{ |
OSStatus retVal = noErr; |
switch(GetEventKind(inEvent)) { |
// This handler creates our custom star frame for our |
// stellar menu. |
case kEventMenuBecomeScrollable : |
case kEventMenuCeaseToBeScrollable : |
break; |
case kEventMenuCreateFrameView : { |
ThemeMenuType menuType; |
HIViewRef frameView; |
verify_noerr(GetEventParameter(inEvent, kEventParamMenuType, typeThemeMenuType, NULL, sizeof(menuType), NULL, &menuType)); |
retVal = CreateStarFrame(data->menu, menuType, &frameView); |
if(noErr == retVal && frameView) { |
verify_noerr(SetEventParameter (inEvent, kEventParamMenuFrameView, typeControlRef, sizeof(frameView), &frameView)); |
retVal = noErr; |
} |
} break; |
} |
return retVal; |
} |
/* --------------------------------------------------- HandleScrollableEvents */ |
/* |
Our menu view should impelement the scrollable protocol as a matter of |
course, but that may not make sense for our star menu. As a result, |
this is a pretty bare-bones implementation of the scrollable protocol. |
*/ |
OSStatus HandleScrollableEvents(EventHandlerCallRef inCallref, EventRef event, StarMenuData* menuData ) |
{ |
OSStatus err = eventNotHandledErr; |
switch (GetEventKind(event)) { |
case kEventScrollableGetInfo: { |
HISize size; |
HIPoint origin = { 0, 0 }; |
size.width = 2 * menuData->radius; |
size.height = 2 * menuData->radius; |
SetEventParameter(event, kEventParamImageSize, typeHISize, sizeof( size ), &size ); |
SetEventParameter(event, kEventParamViewSize, typeHISize, sizeof( size ), &size ); |
SetEventParameter(event, kEventParamOrigin, typeHIPoint, sizeof( origin ), &origin ); |
// line size is 1/10th total size |
size.width /= 10; |
size.height /= 10; |
SetEventParameter(event, kEventParamLineSize, typeHISize, sizeof( size ), &size ); |
err = noErr; |
} break; |
default: |
break; |
} |
return err; |
} |
/* --------------------------------------------------------- DrawStarMenu */ |
/* |
Implementation routine that draws the lovely star menu content |
with Core Graphics. |
*/ |
void DrawStarMenu(EventRef drawEvent, StarMenuData *menuData) |
{ |
HIViewPartCode focusPart; |
CGContextRef cgContext; |
RgnHandle rgnToDraw; |
GetEventParameter(drawEvent, kEventParamCGContextRef, typeCGContextRef, NULL, sizeof(cgContext), NULL, &cgContext ); |
GetEventParameter(drawEvent, kEventParamRgnHandle, typeQDRgnHandle, NULL, sizeof(rgnToDraw), NULL, &rgnToDraw ); |
CGContextSaveGState(cgContext); |
// Flip the coordinate system back over the way God and Euclid intended it to be. |
CGContextTranslateCTM(cgContext, 0, 2 * menuData->radius); |
CGContextScaleCTM(cgContext, 1.0, -1.0); |
// translate the origin of the context to the center of our star; |
CGContextTranslateCTM(cgContext, menuData->radius, menuData->radius); |
// We don't try to optimize the drawing of the menu contents very much. |
// Better behaved code would use the rgnToDraw in order to draw only those |
// items that needed updating. |
// |
// You should do better in your own code :-) |
MenuItemIndex numItems = CountMenuItems(menuData->menu); |
if(numItems > 0) { |
for(short ctr = 1; ctr <= numItems; ctr++) { |
RGBColor itemColor; |
CGPathRef itemPath = CreatePathForStarMenuItem(menuData, ctr); |
GetStarMenuItemColor(menuData->menu, ctr, &itemColor); |
CGContextAddPath(cgContext, itemPath); |
CGPathRelease(itemPath); |
itemPath = NULL; |
CGContextSetRGBFillColor( cgContext, |
(float) itemColor.red / 65535.0, |
(float) itemColor.green / 65535.0, |
(float) itemColor.blue / 65535.0, 1.0); |
CGContextFillPath(cgContext); |
} |
} |
// Draw a nice line around the star. |
CGPathRef entirePath = CreatePathForEntireStarMenu(menuData); |
CGContextAddPath(cgContext, entirePath); |
CGContextSetRGBStrokeColor( cgContext, 0.5, 0.5, 0.5, 1.0); |
CGContextSetLineWidth( cgContext, 0.5); |
CGContextStrokePath(cgContext); |
// Draw a little arrow to show which item is currently selected |
HIViewGetFocusPart( menuData->hiSelf, &focusPart ); |
if(focusPart) { |
float anglePerItem = 2 * pi / (float)numItems; // in radians naturally |
float startAngle = (anglePerItem * (focusPart - 1)); |
float endAngle = (anglePerItem * focusPart) ; |
float midAngle = (startAngle + endAngle) / 2; |
// pick some values for the arrow that look groovy. |
float farRadius = (3.0 * menuData->radius) / 4.0; |
float nearRadius = (3.0 * farRadius) / 5.0; |
CGContextSaveGState(cgContext); |
// It's much easier to rotate the context instead of trying to rotate |
// the points that make up the arrow... Core Graphics is Cool! |
CGContextRotateCTM(cgContext, midAngle); |
CGContextMoveToPoint(cgContext, 0, 0); |
CGContextAddLineToPoint(cgContext, nearRadius, -nearRadius * sin(anglePerItem / 2) * 0.75); |
CGContextAddLineToPoint(cgContext, farRadius, 0); |
CGContextAddLineToPoint(cgContext, nearRadius, nearRadius * sin(anglePerItem / 2) * 0.75); |
CGContextClosePath(cgContext); |
CGContextSetRGBFillColor(cgContext, 1.0, 1.0, 1.0, 0.8); |
CGContextSetRGBStrokeColor(cgContext, 0, 0, 0, 1.0); |
CGContextDrawPath(cgContext, kCGPathFillStroke); |
CGContextRestoreGState(cgContext); |
} |
} |
/* ------------------------------------------------ GetStarMenuPartRegion */ |
/* |
We want to generate regions for our wedges, but regions are a |
QuickDraw artifact and our wedges are drawn with Core Graphics. |
Unfortunately it seems that the the only way to get an accurate region |
from a Core Graphics um... graphic is to draw the Graphic then use |
BitMapToRegion. |
Moreover it is unfortunate that CoreGraphics doesn't like to draw |
in a 1 Bit-per-pixel GWorld. So we draw the Core Graphics wedge |
in full 16 bit color then downsample the pixmap to create something |
suitable for BitMapToRegion |
*/ |
bool GetStarMenuPartRegion( EventRef inEvent, StarMenuData *menuData) |
{ |
Rect qdRect; |
bool retVal = false; |
RgnHandle outRegion = NULL; |
GWorldPtr offscreenWorld = NULL; |
ControlPartCode whichItem; |
MenuItemIndex numItems = CountMenuItems(menuData->menu); |
if(numItems == 0) { |
return false; |
} |
verify_noerr(GetEventParameter(inEvent, kEventParamControlPart, typeControlPartCode, NULL, sizeof(whichItem), NULL, &whichItem)); |
verify_noerr(GetEventParameter(inEvent, kEventParamControlRegion, typeQDRgnHandle, NULL, sizeof(outRegion), NULL, &outRegion)); |
// for all the meta part codes we return the region for the entire control |
if(whichItem < 0) { |
whichItem = kControlEntireControl; |
} |
SetRect(&qdRect, 0, 0, (short)(menuData->radius * 2), (short)(menuData->radius * 2)); |
// Create the GWorld at 8 bits per pixel - grayLevels - for Core Graphics. |
// CoreGraphics uses the gray levels 0...255 in the opposite direction as QD; |
// so we need to fill with black (instead of erase) and invert the result, before |
// processing it into a region. |
OSStatus errStatus = NewGWorld(&offscreenWorld, 8, &qdRect, GetCTable(32 + 8), NULL, 0); |
if(noErr == errStatus && nil != offscreenWorld) { |
GWorldPtr savePort; |
bool swapped = QDSwapPort(offscreenWorld, &savePort); |
PaintRect(&qdRect); |
CGContextRef cgContext = NULL; |
errStatus = QDBeginCGContext(offscreenWorld, &cgContext); |
if(noErr == errStatus && NULL != cgContext) { |
CGContextTranslateCTM(cgContext, menuData->radius, menuData->radius); |
// Create the path for the item we're interested in |
// and make it the current path in the context |
CGPathRef itemPath = NULL; |
if(whichItem != kControlEntireControl) { |
itemPath = CreatePathForStarMenuItem(menuData, whichItem); |
} else { |
itemPath = CreatePathForEntireStarMenu(menuData); |
} |
CGContextAddPath(cgContext, itemPath); |
CGPathRelease(itemPath); |
itemPath = NULL; |
// We add a bit of slop to our region by adding a stroke |
// to the path that generates it. |
CGContextSetRGBFillColor(cgContext, 0, 0, 0, 1.0); |
CGContextSetLineWidth(cgContext, 2.0); |
CGContextSetRGBStrokeColor(cgContext, 0, 0, 0, 1.0); |
CGContextDrawPath(cgContext, kCGPathFillStroke); |
QDEndCGContext(offscreenWorld, &cgContext); |
// Now bring the gray levels back to the QD convention |
InvertRect(&qdRect); |
// Downsample the image to 1 bit-per-pixel |
UpdateGWorld(&offscreenWorld, 1, &qdRect, NULL, NULL, clipPix); |
// Extract the pixmap from the port and the (hopefully) star-shaped region |
// from the pixmap. |
PixMapHandle portPixMap = GetPortPixMap(offscreenWorld); |
if(LockPixels(portPixMap)) { |
verify_noerr(BitMapToRegion(outRegion, (BitMap *) *portPixMap)); |
retVal = true; |
UnlockPixels(portPixMap); |
} |
} |
if(swapped) |
QDSwapPort(savePort, NULL); |
DisposeGWorld(offscreenWorld); |
offscreenWorld = NULL; |
} |
return retVal; |
} |
/* ------------------------------------------------------ HitTestStarMenu */ |
/* |
Remember all that trigonometry you learned? This is why you learned it :-) |
*/ |
void HitTestStarMenu( EventRef inEvent, StarMenuData* menuData) |
{ |
HIPoint mouseLoc; |
ControlPartCode partHit = kControlNoPart; |
MenuItemIndex numItems = CountMenuItems(menuData->menu); |
if(numItems) { |
float anglePerItem = 2 * pi / (float)numItems; // in radians naturally |
verify_noerr(GetEventParameter(inEvent, kEventParamMouseLocation, typeHIPoint, NULL, sizeof( mouseLoc ), NULL, &mouseLoc ) ); |
// Calculate a vector from the center of our view to the mouse location. |
// Because the mouse point is in flipped coordinates this calculation looks a bit off, |
// but is technically correct. Check to see if the mouse is within our star radius |
HIPoint vectorToMouse = { mouseLoc.x - menuData->radius, menuData->radius - mouseLoc.y}; |
bool inCircle = (vectorToMouse.x * vectorToMouse.x + vectorToMouse.y * vectorToMouse.y) <= (menuData->radius * menuData->radius); |
if(inCircle) { |
// find out what angle the mouse makes with the horizontal axis |
// use "man atan2" from the terminal to figure out what it's about |
float mouseAngle = atan2(vectorToMouse.y, vectorToMouse.x); |
// If we got negative mouse angle (i.e. below the |
// horizontal axis) make it positive. |
if(mouseAngle < 0) { |
mouseAngle += 2 * pi; |
} |
// Now the item should come out through simple division |
partHit = (int) (mouseAngle / anglePerItem) + 1; |
} |
} |
SetEventParameter(inEvent, kEventParamControlPart, typeControlPartCode, sizeof( partHit ), &partHit ); |
} |
/* -------------------------------------------- CreatePathForStarMenuItem */ |
/* |
Create a path for the given item. |
In true Core Foundation style, this is a CreateXXX routine and the |
caller is responsible for freeing the path that is returned. |
*/ |
CGPathRef CreatePathForStarMenuItem( |
StarMenuData *menuData, |
MenuItemIndex whichItem) |
{ |
CGMutablePathRef retVal = CGPathCreateMutable(); |
MenuItemIndex numItems = CountMenuItems(menuData->menu); |
if(numItems > 0) { |
const CGPoint fullRadiusPoint = { menuData->radius, 0 }; |
const CGPoint halfRadiusPoint = { menuData->radius / 2.0, 0 }; |
float anglePerItem = 2 * pi / (float)numItems; // in radians naturally |
float halfAngle = anglePerItem / 2.0; |
float startAngle = (anglePerItem * (whichItem - 1)); |
float midAngle = (anglePerItem * (whichItem - 1)) + halfAngle; |
float endAngle = (anglePerItem * whichItem); |
CGAffineTransform startRotate = CGAffineTransformMakeRotation(startAngle); |
CGAffineTransform midRotate = CGAffineTransformMakeRotation(midAngle); |
CGAffineTransform endRotate = CGAffineTransformMakeRotation(endAngle); |
CGPoint startPoint = CGPointApplyAffineTransform(halfRadiusPoint, startRotate); |
CGPoint midPoint = CGPointApplyAffineTransform(fullRadiusPoint, midRotate); |
CGPoint endPoint = CGPointApplyAffineTransform(halfRadiusPoint, endRotate); |
CGPathMoveToPoint(retVal, NULL, 0, 0); |
CGPathAddLineToPoint(retVal, NULL, startPoint.x, startPoint.y); |
CGPathAddLineToPoint(retVal, NULL, midPoint.x, midPoint.y); |
CGPathAddLineToPoint(retVal, NULL, endPoint.x, endPoint.y); |
CGPathCloseSubpath(retVal); |
} |
return retVal; |
} |
/* ------------------------------------------ CreatePathForEntireStarMenu */ |
/* |
Create a path for entire star menu |
In true Core Foundation style, this is a CreateXXX routine and the |
caller is responsible for freeing the path that is returned. |
*/ |
CGPathRef CreatePathForEntireStarMenu(StarMenuData *menuData) |
{ |
CGMutablePathRef retVal = CGPathCreateMutable(); |
MenuItemIndex numItems = CountMenuItems(menuData->menu); |
if(numItems > 0) { |
const CGPoint fullRadiusPoint = { menuData->radius, 0 }; |
const CGPoint halfRadiusPoint = { menuData->radius / 2.0, 0 }; |
float anglePerItem = 2 * pi / (float)numItems; // in radians naturally |
float halfAngle = anglePerItem / 2.0; |
CGPoint startPoint = halfRadiusPoint; |
CGAffineTransform midRotate = CGAffineTransformMakeRotation(halfAngle); |
CGPoint midPoint = CGPointApplyAffineTransform(fullRadiusPoint, midRotate); |
CGAffineTransform rotateToNext = CGAffineTransformMakeRotation(anglePerItem); |
CGPathMoveToPoint(retVal, NULL, startPoint.x, startPoint.y); |
CGPathAddLineToPoint(retVal, NULL, midPoint.x, midPoint.y); |
for(short ctr = 0; ctr < numItems; ctr++) { |
startPoint = CGPointApplyAffineTransform(startPoint, rotateToNext); |
midPoint = CGPointApplyAffineTransform(midPoint, rotateToNext); |
CGPathAddLineToPoint(retVal, NULL, startPoint.x, startPoint.y); |
CGPathAddLineToPoint(retVal, NULL, midPoint.x, midPoint.y); |
} |
CGPathCloseSubpath(retVal); |
} |
return retVal; |
} |
Copyright © 2007 Apple Inc. All Rights Reserved. Terms of Use | Privacy Policy | Updated: 2007-05-24