NSTextFieldCell vertical alignment, solutions seem to squash the horizontal alignment - objective-c

I have a NSTextFieldCell that I wish to display with middle vertical alignment. Thanks to an older question here and a blog entry I have two working solutions.
However, both solutions seem to squash my ability to set the cell as right aligned. Can anyone help me make either of these solutions support both forms of alignment?
Here is the code for one solution:
#implementation MiddleAlignedTextFieldCell
- (NSRect)titleRectForBounds:(NSRect)theRect {
NSRect titleFrame = [super titleRectForBounds:theRect];
NSSize titleSize = [[self attributedStringValue] size];
titleFrame.origin.y = theRect.origin.y - .5 + (theRect.size.height - titleSize.height) / 2.0;
return titleFrame;
}
- (void)drawInteriorWithFrame:(NSRect)cellFrame inView:(NSView *)controlView {
NSRect titleRect = [self titleRectForBounds:cellFrame];
[[self attributedStringValue] drawInRect:titleRect];
}
#end
The alternative solution is (obtained from this blog):
#implementation RSVerticallyCenteredTextFieldCell
- (NSRect)drawingRectForBounds:(NSRect)theRect
{
NSRect newRect = [super drawingRectForBounds:theRect];
if (mIsEditingOrSelecting == NO)
{
// Get our ideal size for current text
NSSize textSize = [self cellSizeForBounds:theRect];
// Center that in the proposed rect
float heightDelta = newRect.size.height - textSize.height;
if (heightDelta > 0)
{
newRect.size.height -= heightDelta;
newRect.origin.y += (heightDelta / 2);
}
}
return newRect;
}
- (void)selectWithFrame:(NSRect)aRect inView:(NSView *)controlView editor:(NSText *)textObj delegate:(id)anObject start:(int)selStart length:(int)selLength
{
aRect = [self drawingRectForBounds:aRect];
mIsEditingOrSelecting = YES;
[super selectWithFrame:aRect inView:controlView editor:textObj delegate:anObject start:selStart length:selLength];
mIsEditingOrSelecting = NO;
}
- (void)editWithFrame:(NSRect)aRect inView:(NSView *)controlView editor:(NSText *)textObj delegate:(id)anObject event:(NSEvent *)theEvent
{
aRect = [self drawingRectForBounds:aRect];
mIsEditingOrSelecting = YES;
[super editWithFrame:aRect inView:controlView editor:textObj delegate:anObject event:theEvent];
mIsEditingOrSelecting = NO;
}
#end

I'm posting this answer to the question since it does work, however, I find the fact that I couldn't find another way to check the alignment setting from IB is very annoying. Accessing _cFlags just seems a little dirty, and I'd love to find a cleaner method.
Based on the code posted earlier from this blog entry.
- (NSRect)drawingRectForBounds:(NSRect)theRect
{
// Get the parent's idea of where we should draw
NSRect newRect = [super drawingRectForBounds:theRect];
if (mIsEditingOrSelecting == NO)
{
// Get our ideal size for current text
NSSize textSize = [self cellSizeForBounds:theRect];
// Center that in the proposed rect
float heightDelta = newRect.size.height - textSize.height;
if (heightDelta > 0)
{
newRect.size.height -= heightDelta;
newRect.origin.y += (heightDelta / 2);
}
// For some reason right aligned text doesn't work. This section makes it work if set in IB.
// HACK: using _cFlags isn't a great idea, but I couldn't find another way to find the alignment.
// TODO: replace _cFlags usage if a better solution is found.
float widthDelta = newRect.size.width - textSize.width;
if (_cFlags.alignment == NSRightTextAlignment && widthDelta > 0) {
newRect.size.width -= widthDelta;
newRect.origin.x += widthDelta;
}
}
return newRect;
}

You can use NSParagraphStyle/NSMutableParagraphStyle to set the alignment (and other attributes). Add an appropriately-configured NSParagraphStyle object to the full range of your attributed string.

There are a couple of potential solutions posted in a similar question which I asked a while back.
In all honesty, I still use the undocumented _cFlags.vCentered boolean (tsk tsk, bad programmer!) to get the job done. It's simple, and it works. I'll reinvent the wheel later on if I have to.
update:
OK, I think I've figured it out. Both solutions rely on a call to super to get the default rect, and then modify origin.y and size.height to perform the vertical centering. The calls to super, however, return a rectangle whose width has already been adjusted to fit the text horizontally.
The solution is to use origin.x and size.width from the bounds rect that is passed in to the method:
In solution #1:
- (NSRect)titleRectForBounds:(NSRect)theRect {
NSRect titleFrame = [super titleRectForBounds:theRect];
NSSize titleSize = [[self attributedStringValue] size];
// modified:
theRect.origin.y += (theRect.size.height - titleSize.height)/2.0 - 0.5;
return theRect;
}
In solution #2:
- (NSRect)drawingRectForBounds:(NSRect)theRect
{
NSRect newRect = [super drawingRectForBounds:theRect];
// modified:
newRect.origin.x = theRect.origin.x;
newRect.size.width = theRect.size.width;
if (mIsEditingOrSelecting == NO)
{
// Get our ideal size for current text
NSSize textSize = [self cellSizeForBounds:theRect];
// Center that in the proposed rect
float heightDelta = newRect.size.height - textSize.height;
if (heightDelta > 0)
{
newRect.size.height -= heightDelta;
newRect.origin.y += (heightDelta / 2);
}
}
return newRect;
}

Related

Cocos2d: Testing for collisions, says that the two sprites always intersect

Hello I am making a cocos2d side scroller. I am trying to test for collisions between two sprites. I checked and the rects I am making for the sprites are what they are supposed to be, but it says that the two rects intersect all of the time whether or not they actually do. Here is the code:
-(void)checkForRedEnemyCollisions{
CGRect playerRect = CGRectMake(player.position.x - (player.playerSprite.contentSize.width/2),
player.position.y - (player.playerSprite.contentSize.height/2),
player.playerSprite.contentSize.width,
player.playerSprite.contentSize.height);
CGRect redEnemyRect = CGRectMake(redEnemy.position.x - (redEnemy.bulletSprite.contentSize.width / 2) ,
redEnemy.position.y - (redEnemy.bulletSprite.contentSize.height /2 ),
redEnemy.bulletSprite.contentSize.width,
redEnemy.bulletSprite.contentSize.height);
if (CGRectIntersectsRect(playerRect, redEnemyRect)) {
CCLOG(#"collision");
}
}
Here is more code:
-(id)init{
if((self = [super init])){
CGSize size = [[CCDirector sharedDirector]winSize];
screenWidth = size.width;
screenHeight = size.height;
gravity = 2;
playerSprite = [CCSprite spriteWithFile:#"thefinalcharacter.png"];
playerSprite.scale = 1.5;
playerSprite.position = ccp(screenWidth/3.4, screenHeight/2);
[self addChild:playerSprite z:-3];
[self schedule: #selector(flight:)interval:1.0f/7.0f];
}
return self;
}
-(void)flight:(ccTime)delta{
flightCounter ++;
if (flightCounter % 2){
[playerSprite setTexture:[[CCSprite spriteWithFile:#"thefinalcharacter.png"]texture]];
}else{
[playerSprite setTexture:[[CCSprite spriteWithFile:#"thefinalcharacter2.png"]texture]];
}
[self schedule:#selector(updatePosition:)interval:1.0f/30.0f];
}
-(void)updatePosition:(ccTime)delta{
if(playerSprite.position.y < 35){
gravity = 0;
}else if(playerSprite.position.y > screenHeight - 150) {
playerSprite.position = ccp(playerSprite.position.x, playerSprite.position.y - 100);
}else{
gravity = 2;
}
playerSprite.position = ccp(playerSprite.position.x, playerSprite.position.y - gravity);
}
make sure that you are creating the bounding rectangles in world coordinates.
try to convert the points to world coordinates like the following;
CGPoint playerWorldPosition = [player ConvertToWorldSpace:player.position];
CGRect playerRect = CGRectMake(playerWorldPosition.x - (player.playerSprite.contentSize.width/2),
playerWorldPosition.y - (player.playerSprite.contentSize.height/2),
player.playerSprite.contentSize.width,
player.playerSprite.contentSize.height);
do the same for redEnemy. this will make sure you are comparing them in world space but not in another space down the hierarchy of layers.
if you using anchor point instead of the default (0.0,0.0) on your player or redEnemies. use the ConvertToWorldSpaceAR function instead of the ConvertToWorldSpace.

NSSplitView Fixed Splitter on Window Resize?

I'm having a bit of difficulty getting a NSSplitView to behave itself.
What I have at the moment is:
NSWindow
NSView
NSSplitView
navView <NSView>
contentView <NSView>
The problem I'm having is with the splitter shifting position when I resize the window.
In the split view delegate I've already got:
-(CGFloat)splitView:(NSSplitView *)splitView constrainMaxCoordinate:(CGFloat)proposedMaximumPosition ofSubviewAt:(NSInteger)dividerIndex{
return 200;
}
-(CGFloat)splitView:(NSSplitView *)splitView constrainMinCoordinate:(CGFloat)proposedMinimumPosition ofSubviewAt:(NSInteger)dividerIndex{
return 200;
}
Despite this the splitter still moved when I resize the window.
if I click ont he splitter, it snaps back to 200px as it should. How can I stop this from moving?
I've tried Autolayout, which is a bit of a nightmare to use, so I've literally disabled it and manually tried to do it with no joy..
Any ideas?
I wrote the above code for Swift and extended it with the possibility to define, whether the left or the right view has to be preferred:
var preferringLeftSideOfSplitView = true
func splitView(splitView: NSSplitView, resizeSubviewsWithOldSize oldSize: NSSize) {
var dividerThickness = splitView.dividerThickness
var leftRect = splitView.subviews[0].frame
var rightRect = splitView.subviews[1].frame
// Resizing and placing the left view
splitView.subviews[0].setFrameOrigin(NSMakePoint(0, 0))
if self.preferringLeftSideOfSplitView == true {
splitView.subviews[0].setFrameSize(NSMakeSize(leftRect.width, splitView.frame.size.height))
} else {
splitView.subviews[0].setFrameSize(NSMakeSize(splitView.frame.size.width - rightRect.width - dividerThickness, splitView.frame.size.height))
}
// Resizing and placing the right view
if self.preferringLeftSideOfSplitView == true {
splitView.subviews[1].setFrameOrigin(NSMakePoint(leftRect.size.width + dividerThickness, 0))
splitView.subviews[1].setFrameSize(NSMakeSize(splitView.frame.size.width - leftRect.size.width - dividerThickness, splitView.frame.size.height))
} else {
splitView.subviews[1].setFrameOrigin(NSMakePoint(splitView.frame.size.width - rightRect.width, 0))
splitView.subviews[1].setFrameSize(NSMakeSize(rightRect.size.width, splitView.frame.size.height))
}
}
I've worked it out...
-(void)splitView:(NSSplitView *)sender resizeSubviewsWithOldSize:(NSSize)oldSize
{
CGFloat dividerThickness = [sender dividerThickness];
NSRect leftRect = [[[sender subviews] objectAtIndex:0] frame];
NSRect rightRect = [[[sender subviews] objectAtIndex:1] frame];
NSRect newFrame = [sender frame];
leftRect.size.height = newFrame.size.height;
leftRect.origin = NSMakePoint(0, 0);
rightRect.size.width = newFrame.size.width - leftRect.size.width
- dividerThickness;
rightRect.size.height = newFrame.size.height;
rightRect.origin.x = leftRect.size.width + dividerThickness;
[[[sender subviews] objectAtIndex:0] setFrame:leftRect];
[[[sender subviews] objectAtIndex:1] setFrame:rightRect];
}
- (BOOL)splitView:(NSSplitView *)aSplitView shouldAdjustSizeOfSubview:(NSView *)subview
{
return subview == rightView;
}
add this to your delegate.
if your NSSplitView would contain lefView and rightView, this keeps leftView at a fixed width, and rightView will resize, when resizing the main window.

Is auto layout system different for NSTableCellViews?

I have created a subclass of NStextField that expands and shrinks to accomodate changes in the text. Here is the code:
- (NSSize)sizeToFitContent
{
NSRect frame = [self frame];
CGFloat width = frame.size.width;
frame.size.height = CGFLOAT_MAX;
CGFloat height = [self.cell cellSizeForBounds: frame].height;
return NSMakeSize(width, height);
}
- (void)textDidChange:(NSNotification *)aNotification
{
[super textDidChange:aNotification];
[(NSCell *)self.cell title];
NSSize newSize = [self sizeToFitContent];
if (newSize.height != self.frame.size.height) {
[self invalidateIntrinsicContentSize];
}
}
- (NSSize)intrinsicContentSize
{
if ( ![self.cell wraps] ) {
return [super intrinsicContentSize];
}
return [self sizeToFitContent];
}
I have verified that the control does what I want it to do in several projects. Recently I tried to place the control inside a NSTableCellView and it stopped working. If I set the stringValue of the control programmatically the control changes its size correctly. However if I type or delete text from the control it does not resize when a new line is needed.
I can't really figure out why the control behaves differently in these two situations?
I believe you will need to implement
- (CGFloat)tableView:(NSTableView *)tableView heightOfRow:(NSInteger)row
in your NSTableViewDelegate.

UIView hitTest:withEvent: and pointInside:withEvent

I'm working with Mixare AR SDK for iOS and I need to solve some bugs that happends, one of them is show the information of a POI when the POI's view is tapped.
Prelude:
Mixare has an overlay UIView within MarkerView views are placed, MarkerView views are moving around the screen to geolocate the POIs and each one has two subviews, an UIImageView and an UILabel.
Issue:
Now, for example, there are 3 visible POIs in the screen, so there are 3 MarkerView as overlay subviews. If you touch anywhere in the overlay, a info view associated to a random POI of which are visible is showed.
Desired:
I want that the associated POI's info is shown only when the user tapped a MarkerView
Let's work. I've see that MarkerView inherits from UIView and implements hitTest:withEvent
- (UIView *)hitTest:(CGPoint)point withEvent:(UIEvent *)event {
viewTouched = (MarkerView*)[super hitTest:point withEvent:event];
return self;
}
I've put a breakpoint and hitTest is called once for each visible MarkerView but loadedView always is null so I can't work with it, so I've tried to check if the hit point is inside the MarkerView frame implementing pointInside:withEvent: by this way
- (BOOL)pointInside:(CGPoint)point withEvent:(UIEvent *)event
{
NSLog(#"ClassName: %#", [[self class] description]);
NSLog(#"Point Inside: %f, %f", point.x, point.y);
NSLog(#"Frame x: %f y: %f widht:%f height:%f", self.frame.origin.x, self.frame.origin.y, self.frame.size.width, self.frame.size.height);
if (CGRectContainsPoint(self.frame, point))
return YES;
else
return NO;
return YES;
}
But this function always returns NO, even when I touch the MarkerView. When I check the log I saw that X and Y point values has negative values sometimes and width and height of the view are very small, 0.00022 or similar instead of 100 x 150 that I set the MarkerView frame on its initialization.
Here you are a extract of my log in which you can see the class name, the point and the MarkerView frame values.
ClassName: MarkerView
2011-12-29 13:20:32.679 paisromanico[2996:707] Point Inside: 105.224899, 49.049023
2011-12-29 13:20:32.683 paisromanico[2996:707] Frame x: 187.568573 y: 245.735138 widht:0.021862 height:0.016427
I'm very lost with this issue so any help will be welcome. Thanks in advance for any help provided and I'm sorry about this brick :(
Edit:
At last I've found that the problem is not in hitTest:withEvent: or pointInside:withEvent, problem is with CGTransform that applies to the MarkerView for scaling based on distande and rotating the view, if I comment any code related to this, the Mixare AR SDK works fine, I mean, info view is shown correctly if you touch a marker and doesn't do anything if any other place in the screen is touched.
So, by the moment, I've not solved the problem but I applied a patch removing the CGTransform related code in AugmentedViewController.m class - (void)updateLocations:(NSTimer *)timer function
- (void)updateLocations:(NSTimer *)timer {
//update locations!
if (!ar_coordinateViews || ar_coordinateViews.count == 0) {
return;
}
int index = 0;
NSMutableArray * radarPointValues= [[NSMutableArray alloc]initWithCapacity:[ar_coordinates count]];
for (PoiItem *item in ar_coordinates) {
MarkerView *viewToDraw = [ar_coordinateViews objectAtIndex:index];
viewToDraw.tag = index;
if ([self viewportContainsCoordinate:item]) {
CGPoint loc = [self pointInView:ar_overlayView forCoordinate:item];
CGFloat scaleFactor = 1.5;
if (self.scaleViewsBasedOnDistance) {
scaleFactor = 1.0 - self.minimumScaleFactor * (item.radialDistance / self.maximumScaleDistance);
}
float width = viewToDraw.bounds.size.width ;//* scaleFactor;
float height = viewToDraw.bounds.size.height; // * scaleFactor;
viewToDraw.frame = CGRectMake(loc.x - width / 2.0, loc.y-height / 2.0, width, height);
/*
CATransform3D transform = CATransform3DIdentity;
//set the scale if it needs it.
if (self.scaleViewsBasedOnDistance) {
//scale the perspective transform if we have one.
transform = CATransform3DScale(transform, scaleFactor, scaleFactor, scaleFactor);
}
if (self.rotateViewsBasedOnPerspective) {
transform.m34 = 1.0 / 300.0;
double itemAzimuth = item.azimuth;
double centerAzimuth = self.centerCoordinate.azimuth;
if (itemAzimuth - centerAzimuth > M_PI) centerAzimuth += 2*M_PI;
if (itemAzimuth - centerAzimuth < -M_PI) itemAzimuth += 2*M_PI;
double angleDifference = itemAzimuth - centerAzimuth;
transform = CATransform3DRotate(transform, self.maximumRotationAngle * angleDifference / (VIEWPORT_HEIGHT_RADIANS / 2.0) , 0, 1, 0);
}
viewToDraw.layer.transform = transform;
*/
//if we don't have a superview, set it up.
if (!(viewToDraw.superview)) {
[ar_overlayView addSubview:viewToDraw];
[ar_overlayView sendSubviewToBack:viewToDraw];
}
} else {
[viewToDraw removeFromSuperview];
viewToDraw.transform = CGAffineTransformIdentity;
}
[radarPointValues addObject:item];
index++;
}
float radius = [[[NSUserDefaults standardUserDefaults] objectForKey:#"radius"] floatValue];
if(radius <= 0 || radius > 100){
radius = 5.0;
}
radarView.pois = radarPointValues;
radarView.radius = radius;
[radarView setNeedsDisplay];
[radarPointValues release];
}
Any CoreGrapics or UI expert could give us his point of view about this issue??
You should either try to hittest as attached:
if ([self pointInside:point withEvent:event]) {
// do something
}
I would suggest you add the hit test on the superview, and do the following in the hit test of the parent of the markerViews
if ([markerView pointInside:point withEvent:event]) {
// extract the tag and show the relevant info
}
Hope this helps

How do I manage the drawing of custom data (not photos) in an NSView that has an area dynamically larger than what is viewable?

UPDATE: Relative to the questions and answers below, it seems I have a misunderstanding of the NSView class in relation to the custom classes I'm trying to draw and the wrapping NSScrollView. In the end, what I'm trying to figure out is how do I manage the dynamic drawing of custom data (not photos) in an NSView that has an area larger than what is viewable?
I am not looking for a handout, but I am a novice to Cocoa and I thought I was doing best-practice based on Apple's docs, but it seems I've gotten the fundamentals wrong. Apple's documentation is incredibly detailed, technical, centered entirely around working with photos, and thus useless to me. The related code examples provided by Apple (e.g. Sketch) get the document size from the printer paper sizes in their typically oblique fashion, and that's not what I need. I've scoured the web for tutorials, examples and the like, but I'm not finding much of anything (and I promise to write one when I get this sorted out).
I'm porting this code from REALbasic where I have this completely working, even with Undo commands, but the paradigms to do so are entirely different. This just isn't "clicking" for me. I appreciate the help given, I'm still missing something here, anything else folks have to offer is appreciated.
Thanks
I have a subclassed NSView where I'm creating a piano-roll MIDI interface. I am trying to resolve a few problems:
Drawing artifacts during and after scrolling
Lines not spanning across the visible area during and after scrolling
While scrolling and sometimes on mouseDown, the horizontal scroller jumps to the right 1 (one) pixel, but I don't have scrollToPoint implemented anywhere yet.
Symptoms that relate to the above:
Implementing adjustScroll makes everything worse.
mouseDown corrects all of the problems except sometimes the 1-pixel jump to the right.
If I uncomment the NSLog command the beginning of drawRect nothing draws.
Apple's documentation mentions pixel-accurate drawing, but (of course) offers up no examples on how this can be achieved. I've been using the floor() function to try to get consistent values, but once I start tacking on scrollToPoint or any other complexity, things go haywire.
Please see the linked image as an example. The screenshot, if you can believe it, actually cleans up what I see on screen. There are double lines almost everywhere at half opacity as well. The same is applied to any objects I draw as well.
Graphics Artifacts and inconsistencies in a subclassed NSView generated after scrolling http://www.oatmealandcoffee.com/external/NSViewArtifacts.png
Here is the code. I hate giving up so much publicly, but I've searched everywhere for clues, and if the Internet is any indication I'm the only person with this problem, and I really just want to get this sorted out and move forward. There is a lot, and there is more to come, but these is the core stuff I really need to get right, and, frankly, I am at a loss on how to correct it.
- (void)drawRect:(NSRect)rect {
//NSLog(#"OCEditorView:drawRect: START");
[self setFrame:[[self EditorDocument] DocumentRect]];
[[NSGraphicsContext currentContext] setShouldAntialias:NO];
// CLEAR BACKGROUND
[[[self EditorDocument] ColorWhiteKey] set];
NSRectFill(rect);
// BACKGROUND KEYS
int firstRowLine = 0; //NSMinY(rect); //<- adding the function results in bad spacing on scrolling
int currentRowLine = 0;
int lastRowLine = NSMaxY(rect);
//NSLog(#"lastRowLine:%d", lastRowLine);
float currentZoomY = [self ZoomY];
for (currentRowLine = firstRowLine; currentRowLine <= lastRowLine; currentRowLine += currentZoomY) {
int currentTone = floor(currentRowLine / [self ZoomY]);
BOOL isBlackKey = [[self MusicLib] IsBlackKey:currentTone];
//NSLog(#"%d, tone:%d, black:%d", [self MusicLib], currentTone, isBlackKey);
if (isBlackKey) {
[[[self EditorDocument] ColorBlackKey] set];
} else {
[[NSColor whiteColor] set];
}
NSBezierPath *rowLine = [NSBezierPath bezierPath];
NSPoint bottomLeftPoint = NSMakePoint(NSMinX(rect), currentRowLine);
NSPoint bottomRightPoint = NSMakePoint(NSMaxX(rect), currentRowLine);
NSPoint topRightPoint = NSMakePoint(NSMaxX(rect), currentRowLine + [self ZoomY]);
NSPoint topLeftPoint = NSMakePoint(NSMinX(rect), currentRowLine + [self ZoomY]);
[rowLine moveToPoint:bottomLeftPoint];
[rowLine lineToPoint:bottomRightPoint];
[rowLine lineToPoint:topRightPoint];
[rowLine lineToPoint:topLeftPoint];
[rowLine closePath];
[rowLine fill];
BOOL isOctave = [[self MusicLib] IsOctave:currentTone];
if (isOctave) {
[[[self EditorDocument] ColorXGrid] set];
NSBezierPath *octaveLine = [NSBezierPath bezierPath];
NSPoint leftPoint = NSMakePoint(NSMinX(rect), currentRowLine);
NSPoint rightPoint = NSMakePoint(NSMaxX(rect), currentRowLine);
[octaveLine moveToPoint:leftPoint];
[octaveLine lineToPoint:rightPoint];
[octaveLine stroke];
}
}
// BACKGROUND MEASURES
//[[self EditorDocument].ColorYGrid setStroke];
int firstColumnLine = 0;
int currentColumnLine = 0;
int lastColumnLine = NSMaxX(rect);
int snapToValueInBeats = [[self EditorDocument] SnapToValue];
int snapToValueInPixels = floor(snapToValueInBeats * [self ZoomX]);
int measureUnitInBeats = floor([[self EditorDocument] TimeSignatureBeatsPerMeasure] * [[self EditorDocument] TimeSignatureBasicBeat]);
int measureUnitInPixels = floor(measureUnitInBeats * [self ZoomX]);
for (currentColumnLine = firstColumnLine; currentColumnLine <= lastColumnLine; currentColumnLine += snapToValueInPixels) {
//int currentBeat = floor(currentColumnLine / [self ZoomX]);
int isAMeasure = currentColumnLine % measureUnitInPixels;
int isAtSnap = currentColumnLine % snapToValueInPixels;
if ((isAMeasure == 0) || (isAtSnap == 0)) {
if (isAtSnap == 0) {
[[NSColor whiteColor] set];
}
if (isAMeasure == 0) {
[[[self EditorDocument] ColorXGrid] set];
}
NSBezierPath *columnLine = [NSBezierPath bezierPath];
NSPoint startPoint = NSMakePoint(currentColumnLine, NSMinY(rect));
NSPoint endPoint = NSMakePoint(currentColumnLine, NSMaxY(rect));
[columnLine moveToPoint:startPoint];
[columnLine lineToPoint:endPoint];
[columnLine setLineWidth:1.0];
[columnLine stroke];
} // isAMeasure or isAtSnap
} // currentColumnLine
// NOTES
for (OCNoteObject *note in [[self EditorDocument] Notes]) {
OCNoteObject *currentNote = note;
NSRect noteBounds = [self GetRectFromNote:currentNote];
//NSLog(#"noteBounds:%d", noteBounds);
// set the color for the note fill
// this will have to come from the parent Track
NSMutableArray *trackColors = [self EditorDocument].TrackColors;
if (note.Selected) {
[[trackColors objectAtIndex:0] set];
} else {
[[trackColors objectAtIndex:1] set];
}
[NSBezierPath fillRect:noteBounds];
// outline
[[NSColor blackColor] set];
[NSBezierPath strokeRect:noteBounds];
} // for each note
/*
if (EditorController.startingUpApplication == YES) {
[self setDefaultSettingForApplicationStartUp];
}
*/
//NSLog(#"OCEditorView:drawRect: END");
}
- (void)mouseDown:(NSEvent *)theEvent {
//NSLog(#"OCEditorObject:mouseDown: START");
// This converts the click into coordinates
MouseDownPoint = [self convertPoint:[theEvent locationInWindow] fromView:nil];
// Calculate the beat and pitch clicked into...
float startBeat = floor(MouseDownPoint.x / [self ZoomX]);
float pitch = floor(MouseDownPoint.y / [self ZoomY]);
float length = [[self EditorDocument] NewNoteLength];
//NSLog(#"X:%f, Y:%f", MouseDownPoint.x, MouseDownPoint.y);
//NSLog(#"beat:%f, pitch:%f", startBeat, pitch);
LastDragPoint = MouseDownPoint; // save the point just in case.
OCNoteObject *note = [self GetClickedNoteFromPoint:MouseDownPoint];
if ([EditorController EditorMode] == AddObjectMode) {
//NSLog(#"AddObjectMode)");
float snapToX = [[self EditorDocument] SnapToValue];
float snappedStartBeat = floor(startBeat / snapToX) * snapToX;
//NSLog(#"%f = %f / %f * %f", snappedStartBeat, startBeat, snapToX, snapToX);
OCNoteObject *newNote = [[self EditorDocument] CreateNote:snappedStartBeat Pitch:pitch Length:length];
//NSLog(#"newNote:%d", newNote);
[newNote Deselect];
} else if ([EditorController EditorMode] == EditObjectMode) {
//NSLog(#"EditObjectMode");
// if nothing was clicked, then clear the selections
// else if the shift key was pressed, add to the selection
if (note == nil) {
[self SelectNone];
} else {
//NSLog(#"mouseDown note.pitch:%f, oldPitch:%f", note.Pitch, note.OldPitch);
BOOL editingSelection = (([theEvent modifierFlags] & NSShiftKeyMask) ? YES : NO);
if (editingSelection) {
if (note.Selected) {
[self RemoveFromSelection:note];
} else {
[self AddToSelection:note];
}
} else {
if (note.Selected) {
// do nothing
} else {
[self SelectNone];
[self AddToSelection:note];
}
}
[self SetOldData];
} // (note == nil)
} else if ([EditorController EditorMode] == DeleteObjectMode) {
if (note != nil) {
[self RemoveFromSelection:note];
[[self EditorDocument] DestroyNote:note];
} // (note != nil)
} // EditorMode
[self setFrame:[[self EditorDocument] DocumentRect]];
[self setNeedsDisplay:YES];
}
- (void)mouseDragged:(NSEvent *)theEvent {
//NSLog(#"mouseDragged");
NSPoint currentDragPoint = [self convertPoint:[theEvent locationInWindow] fromView:nil];
// NSLog(#"currentDragPoint: %d", currentDragPoint)
float snapToValueInBeats = [[self EditorDocument] SnapToValue];
int deltaXinPixels = floor(currentDragPoint.x - MouseDownPoint.x);
int deltaYinPixels = floor(currentDragPoint.y - MouseDownPoint.y);
int deltaXinBeats = floor(deltaXinPixels / [self ZoomX]);
int deltaY = floor(deltaYinPixels / [self ZoomY]);
int deltaX = floor(deltaXinBeats / snapToValueInBeats) * snapToValueInBeats;
for (OCNoteObject *note in [self Selection]) {
[self MoveNote:note DeltaX:deltaX DeltaY:deltaY];
}
LastDragPoint = currentDragPoint;
[self autoscroll:theEvent];
[self setNeedsDisplay:YES]; //artifacts are left if this is off.
}
- (void)mouseUp:(NSEvent *)theEvent {
if ([EditorController EditorMode] == AddObjectMode) {
} else if ([EditorController EditorMode] == EditObjectMode) {
} else if ([EditorController EditorMode] == DeleteObjectMode) {
}
[self setNeedsDisplay:YES];
}
I could very well be missing something obvious, but I think I'm too close to the code to see the solution for what it is. Any help is greatly appreciated! Thanks!
I think you are misunderstanding the way drawRect: and its argument works:
The message drawRect: is sent by cocoa whenever your view or parts of it need to be redrawn. The CGRect argument is the bounding box of all updated areas for the current redraw. That means that you should not derive any positions of objects within your view from this rectangle. It is only passed to the method to allow for optimized drawing: if something is completely outside of this rectangle it does not need to be redrawn.
You should calculate all positions within your view from the views coordinate system: [self bounds]. This does not change each time drawRect: is performed and gives you an origin and size for the contents of the view.
There are a couple of other issues with your code (for instance, don't call setFrame: from within drawRect:) but I think you should first get the coordinates right and then look further into how to calculate pixel-aligned coordinates for your rectangles.
This code of yours looks rather more elaborate than it needs to be. Check out NSCenterScanRect(), and NSRectFillListWithColors(). Also, it's rather wasteful to create and discard paths in -drawRect:.