Create NS/CGRect class which knows its own coordinate space

Originator:mike
Number:rdar://10962421 Date Originated:01-Mar-2012 10:02 AM
Status:Open Resolved:
Product:Mac OS X SDK Product Version:any
Classification:Enhancement Reproducible:Always
 
NSRect/CGRect (and similar structs like NSPoint and NSSize) are error-prone and can be tough to use due to the multitude of coordinate spaces we deal with. Many new Cocoa programmers don't understand the difference between bounds/frame and tend to resort to trial and error to pick the right one. In cases where frame and bounds happen to be numerically similar, an error can persist in code for a long time.

My understanding is that NSRect and friends are structs mainly for efficiency reasons which were a big deal back when this stuff could be expected to run on a 25MHz 68040. This is probably less important today.

I propose a new class, let's call it NSRectangle, which would encompass the functionality of NSRect but would hold not only its origin and size but also its coordinate system. An NSRectangle object could be passed around from one view to another or from one window to another without needing to do any manual conversions, as the object would hold enough information to do them on its own. Something painful and error prone like lining up a window with an NSView's on-screen location would become a simple matter of initializing the window with [view frameRectangle] (assuming -frameRectangle is an equivalent to -frame which returns an NSRectangle object).

There are obviously a lot of compatibility concerns which would require keeping the old structs around pretty much indefinitely. However, I think these new classes could still bring a lot of benefit and supersede the structs in a lot of cases.

01-Mar-2012 03:30 PM Michael Ash:
Here are some code examples of how this class might work and be used:

    // position a subview in the bottom middle of this view
    NSRectangle *frame = [self frameRectangle];
    [frame setHeight: [frame height] / 2];
    [frame setOriginX: [frame width] * 3 / 4];
    [frame setWidth: [frame width] / 2];
    [subview setFrameRectangle: frame];
    
    // position a window directly over this view
    NSRectangle *frame = [self frameRectangle];
    NSWindow *window = [[NSWindow alloc] initWithFrameRectangle: frame ...];
    [window orderFront: nil];
    
    // position a window next to this view on the right
    NSRectangle *frame = [self frameRectangle];
    [frame setOriginX: [frame maxX]];
    [frame setWidth: 42]; // 42 pixels in VIEW COORDINATES, may be different in screen coordinates, but we may want to size the window relative to view coordinates anyway
    NSWindow *window = [[NSWindow alloc] initWithFrameRectangle: frame ...];
    [window orderFront: nil];
    
    // position a window next to this view on the right, sized in screen coords
    NSRectangle *frame = [self frameRectangle];
    [frame setOriginX: [frame maxX]];
    [frame setWidth: 42 inCoordinateSystem: [NSScreen coordinateSystem]];
    NSWindow *window = [[NSWindow alloc] initWithFrameRectangle: frame ...];
    [window orderFront: nil];
    
    // create a 200x200 window with origin at 100,100 on the screen
    NSRectangle *rect = [NSRectangle rectangleWithRect: NSMakeRect(100, 100, 200, 200) inCoordinateSystem: [NSScreen coordinateSystem]];
    NSWindow *window = [[NSWindow alloc] initWithFrameRectangle: rect ...];

    // create a 200x200 window with origin at 100,100 relative to a view
    NSRectangle *rect = [NSRectangle rectangleWithRect: NSMakeRect(100, 100, 200, 200) inCoordinateSystem: [view coordinateSystem]];
    NSWindow *window = [[NSWindow alloc] initWithFrameRectangle: rect ...];
    
    // handling events
    // NSPointObj is a hypothetical class version of the NSPoint struct
    
    // see if a mouse event is within this view
    NSPointObj *loc = [theEvent location];
    if([[self frameRectangle] containsPoint: loc])
        ...
    
    // see if a mouse event is within various views
    NSPointObj *loc = [theEvent location];
    if(([[view1 frameRectangle] containsPoint: loc])
        ...
    else if(([[view2 frameRectangle] containsPoint: loc])
        ...
    else if(([[view3 frameRectangle] containsPoint: loc])
        ...
    // NOTE: view1, view2, and view3 don't need to be siblings for this to work
    // in fact, they don't even need to be in the same window!

Comments

Duplicated: rdar://10969451 on 1-Mar-2012

By heath.borders at March 2, 2012, 4:55 a.m. (reply...)

Duplicated: rdar://10962549 on 5/1/12


Please note: Reports posted here will not necessarily be seen by Apple. All problems should be submitted at bugreport.apple.com before they are posted here. Please only post information for Radars that you have filed yourself, and please do not include Apple confidential information in your posts. Thank you!