How can plugin provide a "Paint(g as Graphics)" event?

This is for OS X (Cocoa).

Suppose we have a Cocoa class, such as NSTableView, which will invoke a draw function like this:

- (void)drawInteriorWithFrame:(NSRect)cellFrame inView:(NSView *)controlView

Now, I’d like to have this call forwarded to a Xojo Event in the form of

Event PaintInterior (g as Graphics, inRect as Rect)

… so that the user can use the Graphics’ class Draw… functions.

Is that possible?

You cannot create Graphics on your own. This has been requested basically for the past 17 years or so, and is large part of why mixing Xojo code and Native code is so hard and has gotten harder over the years and not the other way around.

Couldn’t a workaround be to create a picture in the respective size, forward its graphics object and then copy it to your cell view?

Have you tried

void(*redrawWithRectsFunction)(REALcontrolInstance, REALobject context, const Rect *dirtyRects, int dirtyRectCount);

Thats sort of what has been done. But then Retina came and, its… not good solution.

Not even with TrueWindow.BitmapforCaching?

That would work but lets say Thomas is doing a Table with cells, then you don’t want to be doing such for your cells, your performance would be gone right away, especially if in retina mode where the bitmaps are bigger. Not to mention all the other issues and drama you will hit since the OS was maybe drawing parts already in the area under and then you would come with bitmap and draw over (probably with transparency to not draw over the parts that the OS did draw, making the bitmap even slower)

I wonder how much work it would be to write a replacement Graphics class in Xojo that implements the most-common Draw functions (especially DrawString) and forwards them as CGContext calls or whatever works best.

Also, Björn, do you understand what Alfred suggests (I’m not the plugin author here so I can’t tell) - do you think that would work?

Of course you could create pictures with right resolution and cache them.
Draw them into the view e.g. via showing them using NSImageViewMBS in a view based cells.

Not sure what performance would be by caching thousands of pictures for the cells.

Cam I really draw Pictures during a Cocoa paint event into the current graphics port? I’ll have to experiment with that.

NSGraphicsMBS class. I could add helper functions for Picture direct without NSImageMBS in between.

Alright. Then I’ll probably need a callback (Event) for when a NSCell is drawn. See new eMail.

It gives you the graphics instance that will be valid during drawing. It is connected to the NSView graphics port:

	XGraphics xg = rbContext;
	CGContextRef context = (CGContextRef)xg.handle(5);
	[NSGraphicsContext saveGraphicsState];
	[NSGraphicsContext setCurrentContext:[NSGraphicsContext graphicsContextWithGraphicsPort:context flipped:YES]];
	
	// Figure out where we need to draw
	Rect controlBounds;
	REALGetControlBounds(instance, &controlBounds);
	
	int32_t contextHeight;
	
	//contextHeight = xg.height();
	REALGetPropValueInt32((REALobject)rbContext, "Height", &contextHeight);
	
	// Adjust the co-ordinate system such that the view's origin is where we want
	// it to render
	NSView *view = LexGetHandle(instance);
	NSAffineTransform *transform = [NSAffineTransform transform];
	[transform translateXBy:controlBounds.left yBy:contextHeight - controlBounds.bottom];
	[transform concat];
	
	// Finally we can do some actual drawing!
	[view displayRectIgnoringOpacity:[view bounds] inContext:[NSGraphicsContext currentContext]];
	
	[NSGraphicsContext restoreGraphicsState];

Alfred, that looks more like the Graphics context that Xojo would set up in its own Cocoa-draw handlers, before invoking a Xojo class event. It would allow a plugin to draw, when the plugin function was called from within a Xojo-controlled Event handler.

However, a plugin, when it implements a draw handler itself, can’t use this - or does that work automagically? I mean, rbContext.handle(5) isn’t likely to be set up in my case, because there’s no Xojo code invoked first to set it up, or is that context globally set up once at start?

I would think that if your class is setup as a REALcontrol, your view is presented in this callback. Cocoa gives the graphics as you know during the draw and Xojo provides the context as graphics. You just have to navigate to your sub views.

But if his Graphics was supposed to represent for example a Cell in a NSTableView or something like that then he cannot do much of anything. (Or if it originates from any other such similar thing)

Not sure what you mean. Drawing can only be done if Cocoa is ready to draw. If Cocoa is ready to draw, i.e., the window needs be drawn, all controls on the window will be drawn based on the need. So a graphics context is provided and the draw:: for each control is being called. So Xojo invokes the behavior callback providing an instance of graphics, which is only valid during the draw. Registering a Paint Event for the user is done here.

For a table view you need to provide for each cell a paint event. So that’s up to how you would want to do this, but it should be done here, not anywhere else. The question of this thread is about the Paint event and this can only invoked once Cocoa gives you the graphicscontext, so Xojo can do the same. There is no such thing as windows.graphics. That was in the past.

Alfred, the NSTableView has a callback for each cell to be drawn, so it’s not something my code would perform on its own. I just want to forward the cell-drawing callback from Cocoa to a CellPaint even in Xojo. In all this, Xojo’s framework code is not involved so far, so I am still not clear on how the “rbContext” could be set up during this.

But then again, even with a “normal” plugin control, how does this work? I mean, the RBControl class is a pure Xojo construct. Then there’s a actual native control, usually a NSView subclass (but not in the case of the NSTableView cell case, where it’s a NSCell subclass, i.e. not NSView-related, though all actual drawing will happen in the Tableview’s context, indeed). Now, in the plugin, it would subclass the NSView class and overwrite its drawRect method. Then, when drawRect gets called in the plugin, can it use Alfred’s code example above to forward the call to a “Paint” event of the RBControl, passing a “g as Graphics” parameter so that the Xojo implementation for the even can draw with g.FillRect etc?

Thomas, the REALcontrol on Cocoa is a container for an NSView. Your NSView can contain a number of subviews. All these views share the same CGContextRef. You can have first responders etc that you have to implement. Your CellPaint events should be called when your NSView gets the chance, which is determined by the REALcontrol. The REALcontrol is not a pure Xojo construct in the sense that it encapsulates a true Cocoa view. Therefore drawing of third party REALcontrollers and Xojo REALcontrollers are in essence controlled by the Cocoa framework, guaranteeing drawing to occur when Cocoa and Xojo wants to draw. Yes, Xojo has the last word here. If this is not enough, then I would urge you to create a REALcontrol and do your experiments. Btw, the NSCell in cocoa is an old concept.

Alfred, I appreciate you trying to explain this to me. I must admit that I still only understand half of it as I am not doing any Plugin development. I only started this thread because I want this feature, and Christian said he can’t make it work. Then, here, Björn said so, too. Then you said “try this”. But you never said that you know that this will work - so, do you only suspect it could work, or did you actually manage to implement a “Paint(g as Graphics)” event for a plugin-based control that’s invoked by a NSView’s drawRect or similar overwritten method? Or Björn, did Alfred’s example open your eyes and now you agree that this can be done?

And yes, I undertstand that NSCell-based TableViews are an older concept, but I need it for (a) 10.6 support and (b) because it makes things easier for simulating a ListBox control.