addSubView to NSTableViewCell

Discussion in 'Mac Programming' started by MrFusion, Aug 26, 2011.

  1. MrFusion, Aug 26, 2011
    Last edited: Aug 26, 2011

    MrFusion macrumors 6502a

    Joined:
    Jun 8, 2005
    Location:
    West-Europe
    #1
    Subviews added to a NSTableCellView are incorrectly positioned (see screenshot). But why? :confused:

    This is what I am trying to do. The objectValue set to my custom NSTableCellView has a variable number of data groups. Each data group can be represented by a standard NSControls (checkbox, popupbutton, etc).

    What I did: I have the main xib file with the NSTableView and a xib file with a subview that represents one data group. The NSTableview is configured with a custom NSView.

    This is my code
    Code:
    -(int) heightValue
    {
    //adapt the height based on the number of data groups: x times the height of a subview + spacing
    //do not return 0 !!!
    //	return [self frame].size.height;
    	if ([self objectValue])
    	{
    	//	NSLog(@"d %@",[[self objectValue] description]);
    		return 50+10*[[self objectValue] intValue];//test value
    	}
    	return 16; //default height
    }
    
    -(void) setObjectValue:(id)objectValue
    {
    	[super setObjectValue:objectValue];
    	
            //determine number of required subviews
            ....
    
    	//get a subview (has its own nib file)
    	NSNib *viewNib = [[NSNib alloc] initWithNibNamed:@"CellView" bundle:nil];
    	[viewNib instantiateNibWithOwner:self topLevelObjects:nil];
    	[viewNib release];	
    	NSView *aSubView = [[[self mySubView] retain] autorelease];
    	[self setMySubView:nil];
    	
    	//add subview
    	[self addSubview:aSubView];
    
            ...
    }
    
    - (void)drawRect:(NSRect)dirtyRect
    {
    	[super drawRect:dirtyRect];
    
     	for (NSView *subview in [self subviews]) 
    	{
    		[subview setFrameOrigin:NSMakePoint(0, 0)]; //placed here or in setObjectValue makes no difference
    	}
    		
    	[[self myColor] set];
    	NSRectFill(dirtyRect);
    	
    	NSRect rect = [self frame];
    	
    	[[NSColor orangeColor] set];
    	NSBezierPath *path = [[NSBezierPath alloc] init];
    	[path moveToPoint:CGPointMake(0, 0)];
    	[path lineToPoint:CGPointMake(rect.size.width, rect.size.height)];
    	[path setLineWidth:3];
    	[path stroke];
    	
    }
    
    The background color is an instance variable of the view.
    The orange diagonal line shows I don't have flipped coordinates.
    The actual content of the subview will differ, but these elements (e.g. NSColorWell) have a well defined border and background. Makes it easier to see what is located where.

    Ok, I could play around with setFrameOrigin until it's positioned correctly and do this trial and error for the first x number of subviews. But I would just end up with magic numbers. The origin of the subview within the superview is set to (0,0) and the coordinate system of the sub view is the lower left (see orange diagonal line). And then the NSTableCellView subclass wouldn't work outside of NSTableView.


    Alternatively, I could try to make my own NSControl : NSView and draw different types of cells in the correct position.

    Code:
    	NSPopUpButtonCell *popupCell = [[NSPopUpButtonCell alloc] initTextCell:@"test" pullsDown:YES];
    	[popupCell drawWithFrame:NSMakeRect(0, 0, 99, 22)
    					  inView:self];
    
    The positioning is correct in both the view and tableview. However, it doesn't respond to mouse events. So this means implementing all the mouse tracking and clicking which is a lot of duplicate work. This will probably just end up as badly implemented standard NSControls like NSButton or NSPopupButton.


    Any advice on how to proceed is appreciated!
     

    Attached Files:

  2. MrFusion thread starter macrumors 6502a

    Joined:
    Jun 8, 2005
    Location:
    West-Europe
    #2
    Well, I went ahead with the NSCell approach. The amount of duplicate work does not seem as much as I feared and Apple has some nice examples: "Photosearch" and "ClockControl". Apple's own docs also suggest this approach. I can't find it again, otherwise I would mention which guide or header documentation it was.

    The nib file is simplified to a window with a single NSView element, whose class is set to subclass of NSTableCellView. I can draw several NSTextFieldCells and even edit the text by providing the default field editor.

    However, I am not getting any indication from the field editor whether editing has started, finished or is ongoing. I would like to know when editing has stopped so I can notify the underlying data object and remove the field editor from the view. Everything I could find or think of myself is listed in the code below. Most don't do anything but print out a statement. Yes, it's leaky spaghetti code but I am just trying to find the right approach.

    Any idea what is missing?

    Code:
    
    
    @implementation ComboCell //subclass of NSActionCell
    
    #pragma mark -
    #pragma mark init
    -(id) initWithCoder:(NSCoder *)coder {
        self = [super initWithCoder:coder];
        if (self) 
    	{
    		[self createSubCells];
    	}
        return self;
    }
    
    -(id) init
    {
    	self = [super init];
        if (self) 
    	{
    		[self createSubCells];
    	}
    	return self;
    }
    
    -(void) createSubCells
    {
    	//subcells
    	leftImage = [[NSImageCell alloc] initImageCell:[NSImage imageNamed:@"NSImageNameTrashFull"]];
    	buttonCell = [[NSButtonCell alloc] initTextCell:@"checkbox"];  
    	[buttonCell setButtonType:NSSwitchButton];
    	textCell = [[NSTextFieldCell alloc] initTextCell:@"myTextCell"];
    	rightImage = [[NSImageCell alloc] initImageCell:[NSImage imageNamed:@"NSImageNameTrashFull"]];    
    	
    	//properties
    	[self setEditable:YES];
    }
    
    #pragma mark -
    #pragma mark drawing
    /* draw everthing inside the border */
    - (void)drawInteriorWithFrame:(NSRect)cellFrame 
    					   inView:(NSView *)controlView 
    {
    
    	[textCell drawWithFrame:cellFrame
    					 inView:controlView];
    }
    
    #pragma mark -
    #pragma mark editing
    -(void) setEditable:(BOOL)flag
    {
    	[super setEditable:flag];
    	[textCell setEditable:flag];
    	[textCell setWantsNotificationForMarkedText:YES];
    	[textCell setSendsActionOnEndEditing:YES];
    }
    -(void) editWithFrame:(NSRect)aRect inView:(NSView *)controlView editor:(NSText *)textObj delegate:(id)anObject event:(NSEvent *)theEvent
    {
    	
    #if 0
    	[textCell editWithFrame:aRect
    					 inView:controlView
    					 editor:textObj
    				   delegate:anObject 
    					  event:theEvent];
    #endif
    	
    #if 1
    	[textCell selectWithFrame:aRect
    					   inView:controlView 
    					   editor:textObj
    					 delegate:anObject
    						start:3
    					   length:5];
    #endif
    }
    
    -(void) keyDown:(NSEvent *)theEvent
    {
    	NSLog(@"keydown");
    	[super keyDown:theEvent];
    }
    
    
    -(void) updateCell:(NSCell *)aCell
    {
    	NSLog(@"update subcell");
    }
    -(void) endEditing:(NSText *)textObj
    {
    	NSLog(@"endediting");
    	[super endEditing:textObj];
    	
    }
    
    
    - (void)controlTextDidChange:(NSNotification *)aNotification
    {
    	NSLog(@"controlTextDidChange");
    }
    
    - (void)controlTextDidBeginEditing:(NSNotification *)aNotification
    {
    	NSLog(@"controlTextDidBeginEditing");
    }
    
    -(void) controlTextDidEndEditing:(NSNotification *)aNotification
    {
    	NSLog(@"controlTextDidEndEditing");
    }
    
    -(BOOL) control:(NSControl *)control 
    	   textView:(NSTextView *)fieldEditor
    doCommandBySelector:(SEL)commandSelector 
    {
    	NSLog(@"docommand");
    	BOOL retval = NO;
    	if (commandSelector == @selector(insertNewline:)) {
    		retval = YES;
    		[fieldEditor insertNewlineIgnoringFieldEditor:nil];
    	}
    	return retval;
    }
    
    #pragma mark -
    #pragma mark action target
    -(void) setTarget:(id) anObject
    {
    	NSLog(@"settarget");
    	[super setTarget:anObject];
    	[textCell setTarget:anObject];	
    }
    
    -(void) setAction:(SEL) aSelector
    {
    	NSLog(@"setaction");
    	[super setAction:aSelector];
    	[textCell setAction:aSelector];
    }
    
    -(void) setState:(NSInteger) value
    {
    	[super setState:value];
    	[textCell setState:value];	
    }
    
    -(void) setControlView:(NSView *)view
    {
    	[super setControlView:view];
    	[textCell setControlView:view];
    }
    
    @end
    
    
    //////////////////////////////////////////
    //////////////////////////////////////////
    //////////////////////////////////////////
    
    
    @implementation MyCellView //subclass of NSTableCellView
    
    #pragma mark -
    #pragma mark init
    -(void) awakeFromNib
    {
    //testobject
    	MyObject *object = [[MyObject alloc] init];
    	[object setNumber:[NSNumber numberWithInteger:3]];
    	[object setString:@"standalone"];
    	[self setObjectValue:object];
    }
    
    
    
    #pragma mark -
    #pragma mark object value
    -(void) setObjectValue:(id) objectValue
    {
    	//sanity check
    	if ([self objectValue] == objectValue)
    		return;
    	
    	if (![objectValue isKindOfClass:[MyObject class]])
    		return;
    	
    	//default 
    	[super setObjectValue:objectValue];
    		
    	//add cells	
    	cells = [[NSMutableArray alloc] initWithCapacity:[self cellCount]];	
    	for (int i = 0; i < [self cellCount]; i++)
        {
    		ComboCell *aCell = [[ComboCell alloc] init];
    		[aCell setTarget:self];
    		[aCell setAction:@selector(doSomething:)];
    		[aCell setControlView:self];
    		[aCell setTag:i];	
    		[cells addObject:aCell];
        }
    	
    }
    
    @dynamic cellCount;
    -(NSUInteger) cellCount
    {
    	return [[[self objectValue] valueForKey:@"number"] integerValue];
    }
    
    
    
    #pragma mark -
    #pragma mark drawing
    -(void) drawRect:(NSRect) rect
    {	
    	//draw background: clear everything
    	[[NSColor whiteColor] set];
    	NSRectFill([self bounds]);
    
    	//initial cell position & spacing
    	NSRect cellRect = NSMakeRect(0, 0, 99, 17);
    
    	//draw cells
    	for (int i = 0; i < [cells count]; i++)
    	{		
    		cellRect.origin.y = rect.size.height - (i+1)*30;	
    		if (cellRect.origin.y > 0)
    		{
    			id aCell = [cells objectAtIndex:i];
    			[aCell drawWithFrame:cellRect
    						  inView:self];
    		}
    	}
    }
    
    
    #pragma mark -
    #pragma mark target action
    -(IBAction)doSomething:(id)sender
    {
    	NSLog(@"doSomething:");
    }
    
    
    -(void) keyDown:(NSEvent *)theEvent
    {
    	NSLog(@"keydown");
    	[super keyDown:theEvent];
    }
    
    #pragma mark -
    #pragma mark mouse events
    -(void) mouseDown:(NSEvent *)theEvent
    {
    	
    	NSText *aFieldEditor = [[self window] fieldEditor:YES forObject:nil];
    	NSNotificationCenter *nc = [NSNotificationCenter defaultCenter];
    	[nc addObserver:self
    		   selector:@selector(textDidEndEditing:)
    			   name:NSTextDidEndEditingNotification
    			 object:aFieldEditor];
    	
    	[aFieldEditor setDelegate:self];
    	
    	[[cells objectAtIndex:0] editWithFrame:NSMakeRect(0, [self frame].size.height-30, 99, 17)
    									inView:self
    									editor:aFieldEditor
    								  delegate:self
    									 event:theEvent];
    	
    	[[cells objectAtIndex:0] performClick:self]; //is working, will call doSomething on click
    }
    
    -(void) selectCell:(NSCell *)aCell
    {
    	[[cells objectAtIndex:0] setState:NSOnState];
    }
    
    
    - (void)controlTextDidChange:(NSNotification *)aNotification
    {
    	NSLog(@"controlTextDidChange");
    }
    
    - (void)controlTextDidBeginEditing:(NSNotification *)aNotification
    {
    	NSLog(@"controlTextDidBeginEditing");
    }
    
    -(void) controlTextDidEndEditing:(NSNotification *)aNotification
    {
    	NSLog(@"controlTextDidEndEditing");
    }
    
    -(void) updateCell:(NSCell *)aCell
    {
    	NSLog(@"update cell");
    }
    
    -(BOOL) control:(NSControl *)control 
    	   textView:(NSTextView *)fieldEditor
      doCommandBySelector:(SEL)commandSelector 
    {
    	NSLog(@"docommand");
    	BOOL retval = NO;
    	if (commandSelector == @selector(insertNewline:)) {
    		retval = YES;
    		[fieldEditor insertNewlineIgnoringFieldEditor:nil];
    	}
    	return retval;
    }
    
    @end
    
    
    
     

Share This Page